Commit 86379b44

Stainless Bot <107565488+stainless-bot@users.noreply.github.com>
2024-01-15 22:11:27
feat(client): add support for streaming raw responses (#1072)
As an alternative to `with_raw_response` we now provide `with_streaming_response` as well. When using these methods you will have to use a context manager to ensure that the response is always cleaned up.
1 parent ac33853
examples/audio.py
@@ -12,14 +12,18 @@ speech_file_path = Path(__file__).parent / "speech.mp3"
 
 def main() -> None:
     # Create text-to-speech audio file
-    response = openai.audio.speech.create(
-        model="tts-1", voice="alloy", input="the quick brown fox jumped over the lazy dogs"
-    )
-
-    response.stream_to_file(speech_file_path)
+    with openai.audio.speech.with_streaming_response.create(
+        model="tts-1",
+        voice="alloy",
+        input="the quick brown fox jumped over the lazy dogs",
+    ) as response:
+        response.stream_to_file(speech_file_path)
 
     # Create transcription from audio file
-    transcription = openai.audio.transcriptions.create(model="whisper-1", file=speech_file_path)
+    transcription = openai.audio.transcriptions.create(
+        model="whisper-1",
+        file=speech_file_path,
+    )
     print(transcription.text)
 
     # Create translation from audio file
src/openai/resources/audio/__init__.py
@@ -1,13 +1,36 @@
 # 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, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse
+from .audio import (
+    Audio,
+    AsyncAudio,
+    AudioWithRawResponse,
+    AsyncAudioWithRawResponse,
+    AudioWithStreamingResponse,
+    AsyncAudioWithStreamingResponse,
+)
+from .speech import (
+    Speech,
+    AsyncSpeech,
+    SpeechWithRawResponse,
+    AsyncSpeechWithRawResponse,
+    SpeechWithStreamingResponse,
+    AsyncSpeechWithStreamingResponse,
+)
+from .translations import (
+    Translations,
+    AsyncTranslations,
+    TranslationsWithRawResponse,
+    AsyncTranslationsWithRawResponse,
+    TranslationsWithStreamingResponse,
+    AsyncTranslationsWithStreamingResponse,
+)
 from .transcriptions import (
     Transcriptions,
     AsyncTranscriptions,
     TranscriptionsWithRawResponse,
     AsyncTranscriptionsWithRawResponse,
+    TranscriptionsWithStreamingResponse,
+    AsyncTranscriptionsWithStreamingResponse,
 )
 
 __all__ = [
@@ -15,16 +38,24 @@ __all__ = [
     "AsyncTranscriptions",
     "TranscriptionsWithRawResponse",
     "AsyncTranscriptionsWithRawResponse",
+    "TranscriptionsWithStreamingResponse",
+    "AsyncTranscriptionsWithStreamingResponse",
     "Translations",
     "AsyncTranslations",
     "TranslationsWithRawResponse",
     "AsyncTranslationsWithRawResponse",
+    "TranslationsWithStreamingResponse",
+    "AsyncTranslationsWithStreamingResponse",
     "Speech",
     "AsyncSpeech",
     "SpeechWithRawResponse",
     "AsyncSpeechWithRawResponse",
+    "SpeechWithStreamingResponse",
+    "AsyncSpeechWithStreamingResponse",
     "Audio",
     "AsyncAudio",
     "AudioWithRawResponse",
     "AsyncAudioWithRawResponse",
+    "AudioWithStreamingResponse",
+    "AsyncAudioWithStreamingResponse",
 ]
src/openai/resources/audio/audio.py
@@ -2,15 +2,31 @@
 
 from __future__ import annotations
 
-from .speech import Speech, AsyncSpeech, SpeechWithRawResponse, AsyncSpeechWithRawResponse
+from .speech import (
+    Speech,
+    AsyncSpeech,
+    SpeechWithRawResponse,
+    AsyncSpeechWithRawResponse,
+    SpeechWithStreamingResponse,
+    AsyncSpeechWithStreamingResponse,
+)
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
-from .translations import Translations, AsyncTranslations, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse
+from .translations import (
+    Translations,
+    AsyncTranslations,
+    TranslationsWithRawResponse,
+    AsyncTranslationsWithRawResponse,
+    TranslationsWithStreamingResponse,
+    AsyncTranslationsWithStreamingResponse,
+)
 from .transcriptions import (
     Transcriptions,
     AsyncTranscriptions,
     TranscriptionsWithRawResponse,
     AsyncTranscriptionsWithRawResponse,
+    TranscriptionsWithStreamingResponse,
+    AsyncTranscriptionsWithStreamingResponse,
 )
 
 __all__ = ["Audio", "AsyncAudio"]
@@ -33,6 +49,10 @@ class Audio(SyncAPIResource):
     def with_raw_response(self) -> AudioWithRawResponse:
         return AudioWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AudioWithStreamingResponse:
+        return AudioWithStreamingResponse(self)
+
 
 class AsyncAudio(AsyncAPIResource):
     @cached_property
@@ -51,6 +71,10 @@ class AsyncAudio(AsyncAPIResource):
     def with_raw_response(self) -> AsyncAudioWithRawResponse:
         return AsyncAudioWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncAudioWithStreamingResponse:
+        return AsyncAudioWithStreamingResponse(self)
+
 
 class AudioWithRawResponse:
     def __init__(self, audio: Audio) -> None:
@@ -64,3 +88,17 @@ class AsyncAudioWithRawResponse:
         self.transcriptions = AsyncTranscriptionsWithRawResponse(audio.transcriptions)
         self.translations = AsyncTranslationsWithRawResponse(audio.translations)
         self.speech = AsyncSpeechWithRawResponse(audio.speech)
+
+
+class AudioWithStreamingResponse:
+    def __init__(self, audio: Audio) -> None:
+        self.transcriptions = TranscriptionsWithStreamingResponse(audio.transcriptions)
+        self.translations = TranslationsWithStreamingResponse(audio.translations)
+        self.speech = SpeechWithStreamingResponse(audio.speech)
+
+
+class AsyncAudioWithStreamingResponse:
+    def __init__(self, audio: AsyncAudio) -> None:
+        self.transcriptions = AsyncTranscriptionsWithStreamingResponse(audio.transcriptions)
+        self.translations = AsyncTranslationsWithStreamingResponse(audio.translations)
+        self.speech = AsyncSpeechWithStreamingResponse(audio.speech)
src/openai/resources/audio/speech.py
@@ -7,14 +7,19 @@ from typing_extensions import Literal
 
 import httpx
 
+from ... import _legacy_response
 from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from ..._utils import maybe_transform
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ..._response import (
+    StreamedBinaryAPIResponse,
+    AsyncStreamedBinaryAPIResponse,
+    to_custom_streamed_response_wrapper,
+    async_to_custom_streamed_response_wrapper,
+)
 from ...types.audio import speech_create_params
 from ..._base_client import (
-    HttpxBinaryResponseContent,
     make_request_options,
 )
 
@@ -26,6 +31,10 @@ class Speech(SyncAPIResource):
     def with_raw_response(self) -> SpeechWithRawResponse:
         return SpeechWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> SpeechWithStreamingResponse:
+        return SpeechWithStreamingResponse(self)
+
     def create(
         self,
         *,
@@ -40,7 +49,7 @@ class Speech(SyncAPIResource):
         extra_query: Query | None = None,
         extra_body: Body | None = None,
         timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
-    ) -> HttpxBinaryResponseContent:
+    ) -> _legacy_response.HttpxBinaryResponseContent:
         """
         Generates audio from the input text.
 
@@ -84,7 +93,7 @@ class Speech(SyncAPIResource):
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
-            cast_to=HttpxBinaryResponseContent,
+            cast_to=_legacy_response.HttpxBinaryResponseContent,
         )
 
 
@@ -93,6 +102,10 @@ class AsyncSpeech(AsyncAPIResource):
     def with_raw_response(self) -> AsyncSpeechWithRawResponse:
         return AsyncSpeechWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncSpeechWithStreamingResponse:
+        return AsyncSpeechWithStreamingResponse(self)
+
     async def create(
         self,
         *,
@@ -107,7 +120,7 @@ class AsyncSpeech(AsyncAPIResource):
         extra_query: Query | None = None,
         extra_body: Body | None = None,
         timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
-    ) -> HttpxBinaryResponseContent:
+    ) -> _legacy_response.HttpxBinaryResponseContent:
         """
         Generates audio from the input text.
 
@@ -151,19 +164,35 @@ class AsyncSpeech(AsyncAPIResource):
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
-            cast_to=HttpxBinaryResponseContent,
+            cast_to=_legacy_response.HttpxBinaryResponseContent,
         )
 
 
 class SpeechWithRawResponse:
     def __init__(self, speech: Speech) -> None:
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             speech.create,
         )
 
 
 class AsyncSpeechWithRawResponse:
     def __init__(self, speech: AsyncSpeech) -> None:
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            speech.create,
+        )
+
+
+class SpeechWithStreamingResponse:
+    def __init__(self, speech: Speech) -> None:
+        self.create = to_custom_streamed_response_wrapper(
+            speech.create,
+            StreamedBinaryAPIResponse,
+        )
+
+
+class AsyncSpeechWithStreamingResponse:
+    def __init__(self, speech: AsyncSpeech) -> None:
+        self.create = async_to_custom_streamed_response_wrapper(
             speech.create,
+            AsyncStreamedBinaryAPIResponse,
         )
src/openai/resources/audio/transcriptions.py
@@ -7,11 +7,12 @@ from typing_extensions import Literal
 
 import httpx
 
+from ... import _legacy_response
 from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes
 from ..._utils import extract_files, maybe_transform, deepcopy_minimal
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from ...types.audio import Transcription, transcription_create_params
 from ..._base_client import (
     make_request_options,
@@ -25,6 +26,10 @@ class Transcriptions(SyncAPIResource):
     def with_raw_response(self) -> TranscriptionsWithRawResponse:
         return TranscriptionsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> TranscriptionsWithStreamingResponse:
+        return TranscriptionsWithStreamingResponse(self)
+
     def create(
         self,
         *,
@@ -110,6 +115,10 @@ class AsyncTranscriptions(AsyncAPIResource):
     def with_raw_response(self) -> AsyncTranscriptionsWithRawResponse:
         return AsyncTranscriptionsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncTranscriptionsWithStreamingResponse:
+        return AsyncTranscriptionsWithStreamingResponse(self)
+
     async def create(
         self,
         *,
@@ -192,13 +201,27 @@ class AsyncTranscriptions(AsyncAPIResource):
 
 class TranscriptionsWithRawResponse:
     def __init__(self, transcriptions: Transcriptions) -> None:
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             transcriptions.create,
         )
 
 
 class AsyncTranscriptionsWithRawResponse:
     def __init__(self, transcriptions: AsyncTranscriptions) -> None:
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            transcriptions.create,
+        )
+
+
+class TranscriptionsWithStreamingResponse:
+    def __init__(self, transcriptions: Transcriptions) -> None:
+        self.create = to_streamed_response_wrapper(
+            transcriptions.create,
+        )
+
+
+class AsyncTranscriptionsWithStreamingResponse:
+    def __init__(self, transcriptions: AsyncTranscriptions) -> None:
+        self.create = async_to_streamed_response_wrapper(
             transcriptions.create,
         )
src/openai/resources/audio/translations.py
@@ -7,11 +7,12 @@ from typing_extensions import Literal
 
 import httpx
 
+from ... import _legacy_response
 from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes
 from ..._utils import extract_files, maybe_transform, deepcopy_minimal
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from ...types.audio import Translation, translation_create_params
 from ..._base_client import (
     make_request_options,
@@ -25,6 +26,10 @@ class Translations(SyncAPIResource):
     def with_raw_response(self) -> TranslationsWithRawResponse:
         return TranslationsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> TranslationsWithStreamingResponse:
+        return TranslationsWithStreamingResponse(self)
+
     def create(
         self,
         *,
@@ -103,6 +108,10 @@ class AsyncTranslations(AsyncAPIResource):
     def with_raw_response(self) -> AsyncTranslationsWithRawResponse:
         return AsyncTranslationsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncTranslationsWithStreamingResponse:
+        return AsyncTranslationsWithStreamingResponse(self)
+
     async def create(
         self,
         *,
@@ -178,13 +187,27 @@ class AsyncTranslations(AsyncAPIResource):
 
 class TranslationsWithRawResponse:
     def __init__(self, translations: Translations) -> None:
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             translations.create,
         )
 
 
 class AsyncTranslationsWithRawResponse:
     def __init__(self, translations: AsyncTranslations) -> None:
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            translations.create,
+        )
+
+
+class TranslationsWithStreamingResponse:
+    def __init__(self, translations: Translations) -> None:
+        self.create = to_streamed_response_wrapper(
+            translations.create,
+        )
+
+
+class AsyncTranslationsWithStreamingResponse:
+    def __init__(self, translations: AsyncTranslations) -> None:
+        self.create = async_to_streamed_response_wrapper(
             translations.create,
         )
src/openai/resources/beta/assistants/__init__.py
@@ -1,15 +1,33 @@
 # File generated from our OpenAPI spec by Stainless.
 
-from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse
-from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse
+from .files import (
+    Files,
+    AsyncFiles,
+    FilesWithRawResponse,
+    AsyncFilesWithRawResponse,
+    FilesWithStreamingResponse,
+    AsyncFilesWithStreamingResponse,
+)
+from .assistants import (
+    Assistants,
+    AsyncAssistants,
+    AssistantsWithRawResponse,
+    AsyncAssistantsWithRawResponse,
+    AssistantsWithStreamingResponse,
+    AsyncAssistantsWithStreamingResponse,
+)
 
 __all__ = [
     "Files",
     "AsyncFiles",
     "FilesWithRawResponse",
     "AsyncFilesWithRawResponse",
+    "FilesWithStreamingResponse",
+    "AsyncFilesWithStreamingResponse",
     "Assistants",
     "AsyncAssistants",
     "AssistantsWithRawResponse",
     "AsyncAssistantsWithRawResponse",
+    "AssistantsWithStreamingResponse",
+    "AsyncAssistantsWithStreamingResponse",
 ]
src/openai/resources/beta/assistants/assistants.py
@@ -7,12 +7,20 @@ from typing_extensions import Literal
 
 import httpx
 
-from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse
+from .... import _legacy_response
+from .files import (
+    Files,
+    AsyncFiles,
+    FilesWithRawResponse,
+    AsyncFilesWithRawResponse,
+    FilesWithStreamingResponse,
+    AsyncFilesWithStreamingResponse,
+)
 from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from ...._utils import maybe_transform
 from ...._compat import cached_property
 from ...._resource import SyncAPIResource, AsyncAPIResource
-from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from ....pagination import SyncCursorPage, AsyncCursorPage
 from ....types.beta import (
     Assistant,
@@ -38,6 +46,10 @@ class Assistants(SyncAPIResource):
     def with_raw_response(self) -> AssistantsWithRawResponse:
         return AssistantsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AssistantsWithStreamingResponse:
+        return AssistantsWithStreamingResponse(self)
+
     def create(
         self,
         *,
@@ -331,6 +343,10 @@ class AsyncAssistants(AsyncAPIResource):
     def with_raw_response(self) -> AsyncAssistantsWithRawResponse:
         return AsyncAssistantsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncAssistantsWithStreamingResponse:
+        return AsyncAssistantsWithStreamingResponse(self)
+
     async def create(
         self,
         *,
@@ -619,19 +635,19 @@ class AssistantsWithRawResponse:
     def __init__(self, assistants: Assistants) -> None:
         self.files = FilesWithRawResponse(assistants.files)
 
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             assistants.create,
         )
-        self.retrieve = to_raw_response_wrapper(
+        self.retrieve = _legacy_response.to_raw_response_wrapper(
             assistants.retrieve,
         )
-        self.update = to_raw_response_wrapper(
+        self.update = _legacy_response.to_raw_response_wrapper(
             assistants.update,
         )
-        self.list = to_raw_response_wrapper(
+        self.list = _legacy_response.to_raw_response_wrapper(
             assistants.list,
         )
-        self.delete = to_raw_response_wrapper(
+        self.delete = _legacy_response.to_raw_response_wrapper(
             assistants.delete,
         )
 
@@ -640,18 +656,60 @@ class AsyncAssistantsWithRawResponse:
     def __init__(self, assistants: AsyncAssistants) -> None:
         self.files = AsyncFilesWithRawResponse(assistants.files)
 
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            assistants.create,
+        )
+        self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+            assistants.retrieve,
+        )
+        self.update = _legacy_response.async_to_raw_response_wrapper(
+            assistants.update,
+        )
+        self.list = _legacy_response.async_to_raw_response_wrapper(
+            assistants.list,
+        )
+        self.delete = _legacy_response.async_to_raw_response_wrapper(
+            assistants.delete,
+        )
+
+
+class AssistantsWithStreamingResponse:
+    def __init__(self, assistants: Assistants) -> None:
+        self.files = FilesWithStreamingResponse(assistants.files)
+
+        self.create = to_streamed_response_wrapper(
+            assistants.create,
+        )
+        self.retrieve = to_streamed_response_wrapper(
+            assistants.retrieve,
+        )
+        self.update = to_streamed_response_wrapper(
+            assistants.update,
+        )
+        self.list = to_streamed_response_wrapper(
+            assistants.list,
+        )
+        self.delete = to_streamed_response_wrapper(
+            assistants.delete,
+        )
+
+
+class AsyncAssistantsWithStreamingResponse:
+    def __init__(self, assistants: AsyncAssistants) -> None:
+        self.files = AsyncFilesWithStreamingResponse(assistants.files)
+
+        self.create = async_to_streamed_response_wrapper(
             assistants.create,
         )
-        self.retrieve = async_to_raw_response_wrapper(
+        self.retrieve = async_to_streamed_response_wrapper(
             assistants.retrieve,
         )
-        self.update = async_to_raw_response_wrapper(
+        self.update = async_to_streamed_response_wrapper(
             assistants.update,
         )
-        self.list = async_to_raw_response_wrapper(
+        self.list = async_to_streamed_response_wrapper(
             assistants.list,
         )
-        self.delete = async_to_raw_response_wrapper(
+        self.delete = async_to_streamed_response_wrapper(
             assistants.delete,
         )
src/openai/resources/beta/assistants/files.py
@@ -6,11 +6,12 @@ from typing_extensions import Literal
 
 import httpx
 
+from .... import _legacy_response
 from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from ...._utils import maybe_transform
 from ...._compat import cached_property
 from ...._resource import SyncAPIResource, AsyncAPIResource
-from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from ....pagination import SyncCursorPage, AsyncCursorPage
 from ...._base_client import (
     AsyncPaginator,
@@ -26,6 +27,10 @@ class Files(SyncAPIResource):
     def with_raw_response(self) -> FilesWithRawResponse:
         return FilesWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> FilesWithStreamingResponse:
+        return FilesWithStreamingResponse(self)
+
     def create(
         self,
         assistant_id: str,
@@ -203,6 +208,10 @@ class AsyncFiles(AsyncAPIResource):
     def with_raw_response(self) -> AsyncFilesWithRawResponse:
         return AsyncFilesWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncFilesWithStreamingResponse:
+        return AsyncFilesWithStreamingResponse(self)
+
     async def create(
         self,
         assistant_id: str,
@@ -377,31 +386,63 @@ class AsyncFiles(AsyncAPIResource):
 
 class FilesWithRawResponse:
     def __init__(self, files: Files) -> None:
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             files.create,
         )
-        self.retrieve = to_raw_response_wrapper(
+        self.retrieve = _legacy_response.to_raw_response_wrapper(
             files.retrieve,
         )
-        self.list = to_raw_response_wrapper(
+        self.list = _legacy_response.to_raw_response_wrapper(
             files.list,
         )
-        self.delete = to_raw_response_wrapper(
+        self.delete = _legacy_response.to_raw_response_wrapper(
             files.delete,
         )
 
 
 class AsyncFilesWithRawResponse:
     def __init__(self, files: AsyncFiles) -> None:
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            files.create,
+        )
+        self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+            files.retrieve,
+        )
+        self.list = _legacy_response.async_to_raw_response_wrapper(
+            files.list,
+        )
+        self.delete = _legacy_response.async_to_raw_response_wrapper(
+            files.delete,
+        )
+
+
+class FilesWithStreamingResponse:
+    def __init__(self, files: Files) -> None:
+        self.create = to_streamed_response_wrapper(
+            files.create,
+        )
+        self.retrieve = to_streamed_response_wrapper(
+            files.retrieve,
+        )
+        self.list = to_streamed_response_wrapper(
+            files.list,
+        )
+        self.delete = to_streamed_response_wrapper(
+            files.delete,
+        )
+
+
+class AsyncFilesWithStreamingResponse:
+    def __init__(self, files: AsyncFiles) -> None:
+        self.create = async_to_streamed_response_wrapper(
             files.create,
         )
-        self.retrieve = async_to_raw_response_wrapper(
+        self.retrieve = async_to_streamed_response_wrapper(
             files.retrieve,
         )
-        self.list = async_to_raw_response_wrapper(
+        self.list = async_to_streamed_response_wrapper(
             files.list,
         )
-        self.delete = async_to_raw_response_wrapper(
+        self.delete = async_to_streamed_response_wrapper(
             files.delete,
         )
src/openai/resources/beta/threads/messages/__init__.py
@@ -1,15 +1,33 @@
 # File generated from our OpenAPI spec by Stainless.
 
-from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse
-from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse
+from .files import (
+    Files,
+    AsyncFiles,
+    FilesWithRawResponse,
+    AsyncFilesWithRawResponse,
+    FilesWithStreamingResponse,
+    AsyncFilesWithStreamingResponse,
+)
+from .messages import (
+    Messages,
+    AsyncMessages,
+    MessagesWithRawResponse,
+    AsyncMessagesWithRawResponse,
+    MessagesWithStreamingResponse,
+    AsyncMessagesWithStreamingResponse,
+)
 
 __all__ = [
     "Files",
     "AsyncFiles",
     "FilesWithRawResponse",
     "AsyncFilesWithRawResponse",
+    "FilesWithStreamingResponse",
+    "AsyncFilesWithStreamingResponse",
     "Messages",
     "AsyncMessages",
     "MessagesWithRawResponse",
     "AsyncMessagesWithRawResponse",
+    "MessagesWithStreamingResponse",
+    "AsyncMessagesWithStreamingResponse",
 ]
src/openai/resources/beta/threads/messages/files.py
@@ -6,11 +6,12 @@ from typing_extensions import Literal
 
 import httpx
 
+from ..... import _legacy_response
 from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from ....._utils import maybe_transform
 from ....._compat import cached_property
 from ....._resource import SyncAPIResource, AsyncAPIResource
-from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from .....pagination import SyncCursorPage, AsyncCursorPage
 from ....._base_client import (
     AsyncPaginator,
@@ -26,6 +27,10 @@ class Files(SyncAPIResource):
     def with_raw_response(self) -> FilesWithRawResponse:
         return FilesWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> FilesWithStreamingResponse:
+        return FilesWithStreamingResponse(self)
+
     def retrieve(
         self,
         file_id: str,
@@ -133,6 +138,10 @@ class AsyncFiles(AsyncAPIResource):
     def with_raw_response(self) -> AsyncFilesWithRawResponse:
         return AsyncFilesWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncFilesWithStreamingResponse:
+        return AsyncFilesWithStreamingResponse(self)
+
     async def retrieve(
         self,
         file_id: str,
@@ -237,19 +246,39 @@ class AsyncFiles(AsyncAPIResource):
 
 class FilesWithRawResponse:
     def __init__(self, files: Files) -> None:
-        self.retrieve = to_raw_response_wrapper(
+        self.retrieve = _legacy_response.to_raw_response_wrapper(
             files.retrieve,
         )
-        self.list = to_raw_response_wrapper(
+        self.list = _legacy_response.to_raw_response_wrapper(
             files.list,
         )
 
 
 class AsyncFilesWithRawResponse:
     def __init__(self, files: AsyncFiles) -> None:
-        self.retrieve = async_to_raw_response_wrapper(
+        self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+            files.retrieve,
+        )
+        self.list = _legacy_response.async_to_raw_response_wrapper(
+            files.list,
+        )
+
+
+class FilesWithStreamingResponse:
+    def __init__(self, files: Files) -> None:
+        self.retrieve = to_streamed_response_wrapper(
+            files.retrieve,
+        )
+        self.list = to_streamed_response_wrapper(
+            files.list,
+        )
+
+
+class AsyncFilesWithStreamingResponse:
+    def __init__(self, files: AsyncFiles) -> None:
+        self.retrieve = async_to_streamed_response_wrapper(
             files.retrieve,
         )
-        self.list = async_to_raw_response_wrapper(
+        self.list = async_to_streamed_response_wrapper(
             files.list,
         )
src/openai/resources/beta/threads/messages/messages.py
@@ -7,12 +7,20 @@ from typing_extensions import Literal
 
 import httpx
 
-from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse
+from ..... import _legacy_response
+from .files import (
+    Files,
+    AsyncFiles,
+    FilesWithRawResponse,
+    AsyncFilesWithRawResponse,
+    FilesWithStreamingResponse,
+    AsyncFilesWithStreamingResponse,
+)
 from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from ....._utils import maybe_transform
 from ....._compat import cached_property
 from ....._resource import SyncAPIResource, AsyncAPIResource
-from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from .....pagination import SyncCursorPage, AsyncCursorPage
 from ....._base_client import (
     AsyncPaginator,
@@ -32,6 +40,10 @@ class Messages(SyncAPIResource):
     def with_raw_response(self) -> MessagesWithRawResponse:
         return MessagesWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> MessagesWithStreamingResponse:
+        return MessagesWithStreamingResponse(self)
+
     def create(
         self,
         thread_id: str,
@@ -240,6 +252,10 @@ class AsyncMessages(AsyncAPIResource):
     def with_raw_response(self) -> AsyncMessagesWithRawResponse:
         return AsyncMessagesWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse:
+        return AsyncMessagesWithStreamingResponse(self)
+
     async def create(
         self,
         thread_id: str,
@@ -443,16 +459,16 @@ class MessagesWithRawResponse:
     def __init__(self, messages: Messages) -> None:
         self.files = FilesWithRawResponse(messages.files)
 
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             messages.create,
         )
-        self.retrieve = to_raw_response_wrapper(
+        self.retrieve = _legacy_response.to_raw_response_wrapper(
             messages.retrieve,
         )
-        self.update = to_raw_response_wrapper(
+        self.update = _legacy_response.to_raw_response_wrapper(
             messages.update,
         )
-        self.list = to_raw_response_wrapper(
+        self.list = _legacy_response.to_raw_response_wrapper(
             messages.list,
         )
 
@@ -461,15 +477,51 @@ class AsyncMessagesWithRawResponse:
     def __init__(self, messages: AsyncMessages) -> None:
         self.files = AsyncFilesWithRawResponse(messages.files)
 
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            messages.create,
+        )
+        self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+            messages.retrieve,
+        )
+        self.update = _legacy_response.async_to_raw_response_wrapper(
+            messages.update,
+        )
+        self.list = _legacy_response.async_to_raw_response_wrapper(
+            messages.list,
+        )
+
+
+class MessagesWithStreamingResponse:
+    def __init__(self, messages: Messages) -> None:
+        self.files = FilesWithStreamingResponse(messages.files)
+
+        self.create = to_streamed_response_wrapper(
+            messages.create,
+        )
+        self.retrieve = to_streamed_response_wrapper(
+            messages.retrieve,
+        )
+        self.update = to_streamed_response_wrapper(
+            messages.update,
+        )
+        self.list = to_streamed_response_wrapper(
+            messages.list,
+        )
+
+
+class AsyncMessagesWithStreamingResponse:
+    def __init__(self, messages: AsyncMessages) -> None:
+        self.files = AsyncFilesWithStreamingResponse(messages.files)
+
+        self.create = async_to_streamed_response_wrapper(
             messages.create,
         )
-        self.retrieve = async_to_raw_response_wrapper(
+        self.retrieve = async_to_streamed_response_wrapper(
             messages.retrieve,
         )
-        self.update = async_to_raw_response_wrapper(
+        self.update = async_to_streamed_response_wrapper(
             messages.update,
         )
-        self.list = async_to_raw_response_wrapper(
+        self.list = async_to_streamed_response_wrapper(
             messages.list,
         )
src/openai/resources/beta/threads/runs/__init__.py
@@ -1,15 +1,33 @@
 # File generated from our OpenAPI spec by Stainless.
 
-from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse
-from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse
+from .runs import (
+    Runs,
+    AsyncRuns,
+    RunsWithRawResponse,
+    AsyncRunsWithRawResponse,
+    RunsWithStreamingResponse,
+    AsyncRunsWithStreamingResponse,
+)
+from .steps import (
+    Steps,
+    AsyncSteps,
+    StepsWithRawResponse,
+    AsyncStepsWithRawResponse,
+    StepsWithStreamingResponse,
+    AsyncStepsWithStreamingResponse,
+)
 
 __all__ = [
     "Steps",
     "AsyncSteps",
     "StepsWithRawResponse",
     "AsyncStepsWithRawResponse",
+    "StepsWithStreamingResponse",
+    "AsyncStepsWithStreamingResponse",
     "Runs",
     "AsyncRuns",
     "RunsWithRawResponse",
     "AsyncRunsWithRawResponse",
+    "RunsWithStreamingResponse",
+    "AsyncRunsWithStreamingResponse",
 ]
src/openai/resources/beta/threads/runs/runs.py
@@ -7,12 +7,20 @@ from typing_extensions import Literal
 
 import httpx
 
-from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse
+from ..... import _legacy_response
+from .steps import (
+    Steps,
+    AsyncSteps,
+    StepsWithRawResponse,
+    AsyncStepsWithRawResponse,
+    StepsWithStreamingResponse,
+    AsyncStepsWithStreamingResponse,
+)
 from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from ....._utils import maybe_transform
 from ....._compat import cached_property
 from ....._resource import SyncAPIResource, AsyncAPIResource
-from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from .....pagination import SyncCursorPage, AsyncCursorPage
 from ....._base_client import (
     AsyncPaginator,
@@ -38,6 +46,10 @@ class Runs(SyncAPIResource):
     def with_raw_response(self) -> RunsWithRawResponse:
         return RunsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> RunsWithStreamingResponse:
+        return RunsWithStreamingResponse(self)
+
     def create(
         self,
         thread_id: str,
@@ -335,6 +347,10 @@ class AsyncRuns(AsyncAPIResource):
     def with_raw_response(self) -> AsyncRunsWithRawResponse:
         return AsyncRunsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncRunsWithStreamingResponse:
+        return AsyncRunsWithStreamingResponse(self)
+
     async def create(
         self,
         thread_id: str,
@@ -627,22 +643,22 @@ class RunsWithRawResponse:
     def __init__(self, runs: Runs) -> None:
         self.steps = StepsWithRawResponse(runs.steps)
 
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             runs.create,
         )
-        self.retrieve = to_raw_response_wrapper(
+        self.retrieve = _legacy_response.to_raw_response_wrapper(
             runs.retrieve,
         )
-        self.update = to_raw_response_wrapper(
+        self.update = _legacy_response.to_raw_response_wrapper(
             runs.update,
         )
-        self.list = to_raw_response_wrapper(
+        self.list = _legacy_response.to_raw_response_wrapper(
             runs.list,
         )
-        self.cancel = to_raw_response_wrapper(
+        self.cancel = _legacy_response.to_raw_response_wrapper(
             runs.cancel,
         )
-        self.submit_tool_outputs = to_raw_response_wrapper(
+        self.submit_tool_outputs = _legacy_response.to_raw_response_wrapper(
             runs.submit_tool_outputs,
         )
 
@@ -651,21 +667,69 @@ class AsyncRunsWithRawResponse:
     def __init__(self, runs: AsyncRuns) -> None:
         self.steps = AsyncStepsWithRawResponse(runs.steps)
 
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            runs.create,
+        )
+        self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+            runs.retrieve,
+        )
+        self.update = _legacy_response.async_to_raw_response_wrapper(
+            runs.update,
+        )
+        self.list = _legacy_response.async_to_raw_response_wrapper(
+            runs.list,
+        )
+        self.cancel = _legacy_response.async_to_raw_response_wrapper(
+            runs.cancel,
+        )
+        self.submit_tool_outputs = _legacy_response.async_to_raw_response_wrapper(
+            runs.submit_tool_outputs,
+        )
+
+
+class RunsWithStreamingResponse:
+    def __init__(self, runs: Runs) -> None:
+        self.steps = StepsWithStreamingResponse(runs.steps)
+
+        self.create = to_streamed_response_wrapper(
+            runs.create,
+        )
+        self.retrieve = to_streamed_response_wrapper(
+            runs.retrieve,
+        )
+        self.update = to_streamed_response_wrapper(
+            runs.update,
+        )
+        self.list = to_streamed_response_wrapper(
+            runs.list,
+        )
+        self.cancel = to_streamed_response_wrapper(
+            runs.cancel,
+        )
+        self.submit_tool_outputs = to_streamed_response_wrapper(
+            runs.submit_tool_outputs,
+        )
+
+
+class AsyncRunsWithStreamingResponse:
+    def __init__(self, runs: AsyncRuns) -> None:
+        self.steps = AsyncStepsWithStreamingResponse(runs.steps)
+
+        self.create = async_to_streamed_response_wrapper(
             runs.create,
         )
-        self.retrieve = async_to_raw_response_wrapper(
+        self.retrieve = async_to_streamed_response_wrapper(
             runs.retrieve,
         )
-        self.update = async_to_raw_response_wrapper(
+        self.update = async_to_streamed_response_wrapper(
             runs.update,
         )
-        self.list = async_to_raw_response_wrapper(
+        self.list = async_to_streamed_response_wrapper(
             runs.list,
         )
-        self.cancel = async_to_raw_response_wrapper(
+        self.cancel = async_to_streamed_response_wrapper(
             runs.cancel,
         )
-        self.submit_tool_outputs = async_to_raw_response_wrapper(
+        self.submit_tool_outputs = async_to_streamed_response_wrapper(
             runs.submit_tool_outputs,
         )
src/openai/resources/beta/threads/runs/steps.py
@@ -6,11 +6,12 @@ from typing_extensions import Literal
 
 import httpx
 
+from ..... import _legacy_response
 from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from ....._utils import maybe_transform
 from ....._compat import cached_property
 from ....._resource import SyncAPIResource, AsyncAPIResource
-from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from .....pagination import SyncCursorPage, AsyncCursorPage
 from ....._base_client import (
     AsyncPaginator,
@@ -26,6 +27,10 @@ class Steps(SyncAPIResource):
     def with_raw_response(self) -> StepsWithRawResponse:
         return StepsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> StepsWithStreamingResponse:
+        return StepsWithStreamingResponse(self)
+
     def retrieve(
         self,
         step_id: str,
@@ -132,6 +137,10 @@ class AsyncSteps(AsyncAPIResource):
     def with_raw_response(self) -> AsyncStepsWithRawResponse:
         return AsyncStepsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncStepsWithStreamingResponse:
+        return AsyncStepsWithStreamingResponse(self)
+
     async def retrieve(
         self,
         step_id: str,
@@ -235,19 +244,39 @@ class AsyncSteps(AsyncAPIResource):
 
 class StepsWithRawResponse:
     def __init__(self, steps: Steps) -> None:
-        self.retrieve = to_raw_response_wrapper(
+        self.retrieve = _legacy_response.to_raw_response_wrapper(
             steps.retrieve,
         )
-        self.list = to_raw_response_wrapper(
+        self.list = _legacy_response.to_raw_response_wrapper(
             steps.list,
         )
 
 
 class AsyncStepsWithRawResponse:
     def __init__(self, steps: AsyncSteps) -> None:
-        self.retrieve = async_to_raw_response_wrapper(
+        self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+            steps.retrieve,
+        )
+        self.list = _legacy_response.async_to_raw_response_wrapper(
+            steps.list,
+        )
+
+
+class StepsWithStreamingResponse:
+    def __init__(self, steps: Steps) -> None:
+        self.retrieve = to_streamed_response_wrapper(
+            steps.retrieve,
+        )
+        self.list = to_streamed_response_wrapper(
+            steps.list,
+        )
+
+
+class AsyncStepsWithStreamingResponse:
+    def __init__(self, steps: AsyncSteps) -> None:
+        self.retrieve = async_to_streamed_response_wrapper(
             steps.retrieve,
         )
-        self.list = async_to_raw_response_wrapper(
+        self.list = async_to_streamed_response_wrapper(
             steps.list,
         )
src/openai/resources/beta/threads/__init__.py
@@ -1,20 +1,47 @@
 # 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
+from .runs import (
+    Runs,
+    AsyncRuns,
+    RunsWithRawResponse,
+    AsyncRunsWithRawResponse,
+    RunsWithStreamingResponse,
+    AsyncRunsWithStreamingResponse,
+)
+from .threads import (
+    Threads,
+    AsyncThreads,
+    ThreadsWithRawResponse,
+    AsyncThreadsWithRawResponse,
+    ThreadsWithStreamingResponse,
+    AsyncThreadsWithStreamingResponse,
+)
+from .messages import (
+    Messages,
+    AsyncMessages,
+    MessagesWithRawResponse,
+    AsyncMessagesWithRawResponse,
+    MessagesWithStreamingResponse,
+    AsyncMessagesWithStreamingResponse,
+)
 
 __all__ = [
     "Runs",
     "AsyncRuns",
     "RunsWithRawResponse",
     "AsyncRunsWithRawResponse",
+    "RunsWithStreamingResponse",
+    "AsyncRunsWithStreamingResponse",
     "Messages",
     "AsyncMessages",
     "MessagesWithRawResponse",
     "AsyncMessagesWithRawResponse",
+    "MessagesWithStreamingResponse",
+    "AsyncMessagesWithStreamingResponse",
     "Threads",
     "AsyncThreads",
     "ThreadsWithRawResponse",
     "AsyncThreadsWithRawResponse",
+    "ThreadsWithStreamingResponse",
+    "AsyncThreadsWithStreamingResponse",
 ]
src/openai/resources/beta/threads/threads.py
@@ -6,14 +6,29 @@ from typing import List, Optional
 
 import httpx
 
-from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse
-from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse
+from .... import _legacy_response
+from .runs import (
+    Runs,
+    AsyncRuns,
+    RunsWithRawResponse,
+    AsyncRunsWithRawResponse,
+    RunsWithStreamingResponse,
+    AsyncRunsWithStreamingResponse,
+)
+from .messages import (
+    Messages,
+    AsyncMessages,
+    MessagesWithRawResponse,
+    AsyncMessagesWithRawResponse,
+    MessagesWithStreamingResponse,
+    AsyncMessagesWithStreamingResponse,
+)
 from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from ...._utils import maybe_transform
 from .runs.runs import Runs, AsyncRuns
 from ...._compat import cached_property
 from ...._resource import SyncAPIResource, AsyncAPIResource
-from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from ....types.beta import (
     Thread,
     ThreadDeleted,
@@ -43,6 +58,10 @@ class Threads(SyncAPIResource):
     def with_raw_response(self) -> ThreadsWithRawResponse:
         return ThreadsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> ThreadsWithStreamingResponse:
+        return ThreadsWithStreamingResponse(self)
+
     def create(
         self,
         *,
@@ -278,6 +297,10 @@ class AsyncThreads(AsyncAPIResource):
     def with_raw_response(self) -> AsyncThreadsWithRawResponse:
         return AsyncThreadsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncThreadsWithStreamingResponse:
+        return AsyncThreadsWithStreamingResponse(self)
+
     async def create(
         self,
         *,
@@ -505,19 +528,19 @@ class ThreadsWithRawResponse:
         self.runs = RunsWithRawResponse(threads.runs)
         self.messages = MessagesWithRawResponse(threads.messages)
 
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             threads.create,
         )
-        self.retrieve = to_raw_response_wrapper(
+        self.retrieve = _legacy_response.to_raw_response_wrapper(
             threads.retrieve,
         )
-        self.update = to_raw_response_wrapper(
+        self.update = _legacy_response.to_raw_response_wrapper(
             threads.update,
         )
-        self.delete = to_raw_response_wrapper(
+        self.delete = _legacy_response.to_raw_response_wrapper(
             threads.delete,
         )
-        self.create_and_run = to_raw_response_wrapper(
+        self.create_and_run = _legacy_response.to_raw_response_wrapper(
             threads.create_and_run,
         )
 
@@ -527,18 +550,62 @@ class AsyncThreadsWithRawResponse:
         self.runs = AsyncRunsWithRawResponse(threads.runs)
         self.messages = AsyncMessagesWithRawResponse(threads.messages)
 
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            threads.create,
+        )
+        self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+            threads.retrieve,
+        )
+        self.update = _legacy_response.async_to_raw_response_wrapper(
+            threads.update,
+        )
+        self.delete = _legacy_response.async_to_raw_response_wrapper(
+            threads.delete,
+        )
+        self.create_and_run = _legacy_response.async_to_raw_response_wrapper(
+            threads.create_and_run,
+        )
+
+
+class ThreadsWithStreamingResponse:
+    def __init__(self, threads: Threads) -> None:
+        self.runs = RunsWithStreamingResponse(threads.runs)
+        self.messages = MessagesWithStreamingResponse(threads.messages)
+
+        self.create = to_streamed_response_wrapper(
+            threads.create,
+        )
+        self.retrieve = to_streamed_response_wrapper(
+            threads.retrieve,
+        )
+        self.update = to_streamed_response_wrapper(
+            threads.update,
+        )
+        self.delete = to_streamed_response_wrapper(
+            threads.delete,
+        )
+        self.create_and_run = to_streamed_response_wrapper(
+            threads.create_and_run,
+        )
+
+
+class AsyncThreadsWithStreamingResponse:
+    def __init__(self, threads: AsyncThreads) -> None:
+        self.runs = AsyncRunsWithStreamingResponse(threads.runs)
+        self.messages = AsyncMessagesWithStreamingResponse(threads.messages)
+
+        self.create = async_to_streamed_response_wrapper(
             threads.create,
         )
-        self.retrieve = async_to_raw_response_wrapper(
+        self.retrieve = async_to_streamed_response_wrapper(
             threads.retrieve,
         )
-        self.update = async_to_raw_response_wrapper(
+        self.update = async_to_streamed_response_wrapper(
             threads.update,
         )
-        self.delete = async_to_raw_response_wrapper(
+        self.delete = async_to_streamed_response_wrapper(
             threads.delete,
         )
-        self.create_and_run = async_to_raw_response_wrapper(
+        self.create_and_run = async_to_streamed_response_wrapper(
             threads.create_and_run,
         )
src/openai/resources/beta/__init__.py
@@ -1,20 +1,47 @@
 # 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
+from .beta import (
+    Beta,
+    AsyncBeta,
+    BetaWithRawResponse,
+    AsyncBetaWithRawResponse,
+    BetaWithStreamingResponse,
+    AsyncBetaWithStreamingResponse,
+)
+from .threads import (
+    Threads,
+    AsyncThreads,
+    ThreadsWithRawResponse,
+    AsyncThreadsWithRawResponse,
+    ThreadsWithStreamingResponse,
+    AsyncThreadsWithStreamingResponse,
+)
+from .assistants import (
+    Assistants,
+    AsyncAssistants,
+    AssistantsWithRawResponse,
+    AsyncAssistantsWithRawResponse,
+    AssistantsWithStreamingResponse,
+    AsyncAssistantsWithStreamingResponse,
+)
 
 __all__ = [
     "Assistants",
     "AsyncAssistants",
     "AssistantsWithRawResponse",
     "AsyncAssistantsWithRawResponse",
+    "AssistantsWithStreamingResponse",
+    "AsyncAssistantsWithStreamingResponse",
     "Threads",
     "AsyncThreads",
     "ThreadsWithRawResponse",
     "AsyncThreadsWithRawResponse",
+    "ThreadsWithStreamingResponse",
+    "AsyncThreadsWithStreamingResponse",
     "Beta",
     "AsyncBeta",
     "BetaWithRawResponse",
     "AsyncBetaWithRawResponse",
+    "BetaWithStreamingResponse",
+    "AsyncBetaWithStreamingResponse",
 ]
src/openai/resources/beta/beta.py
@@ -2,9 +2,23 @@
 
 from __future__ import annotations
 
-from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse
+from .threads import (
+    Threads,
+    AsyncThreads,
+    ThreadsWithRawResponse,
+    AsyncThreadsWithRawResponse,
+    ThreadsWithStreamingResponse,
+    AsyncThreadsWithStreamingResponse,
+)
 from ..._compat import cached_property
-from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse
+from .assistants import (
+    Assistants,
+    AsyncAssistants,
+    AssistantsWithRawResponse,
+    AsyncAssistantsWithRawResponse,
+    AssistantsWithStreamingResponse,
+    AsyncAssistantsWithStreamingResponse,
+)
 from ..._resource import SyncAPIResource, AsyncAPIResource
 from .threads.threads import Threads, AsyncThreads
 from .assistants.assistants import Assistants, AsyncAssistants
@@ -25,6 +39,10 @@ class Beta(SyncAPIResource):
     def with_raw_response(self) -> BetaWithRawResponse:
         return BetaWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> BetaWithStreamingResponse:
+        return BetaWithStreamingResponse(self)
+
 
 class AsyncBeta(AsyncAPIResource):
     @cached_property
@@ -39,6 +57,10 @@ class AsyncBeta(AsyncAPIResource):
     def with_raw_response(self) -> AsyncBetaWithRawResponse:
         return AsyncBetaWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncBetaWithStreamingResponse:
+        return AsyncBetaWithStreamingResponse(self)
+
 
 class BetaWithRawResponse:
     def __init__(self, beta: Beta) -> None:
@@ -50,3 +72,15 @@ class AsyncBetaWithRawResponse:
     def __init__(self, beta: AsyncBeta) -> None:
         self.assistants = AsyncAssistantsWithRawResponse(beta.assistants)
         self.threads = AsyncThreadsWithRawResponse(beta.threads)
+
+
+class BetaWithStreamingResponse:
+    def __init__(self, beta: Beta) -> None:
+        self.assistants = AssistantsWithStreamingResponse(beta.assistants)
+        self.threads = ThreadsWithStreamingResponse(beta.threads)
+
+
+class AsyncBetaWithStreamingResponse:
+    def __init__(self, beta: AsyncBeta) -> None:
+        self.assistants = AsyncAssistantsWithStreamingResponse(beta.assistants)
+        self.threads = AsyncThreadsWithStreamingResponse(beta.threads)
src/openai/resources/chat/__init__.py
@@ -1,15 +1,33 @@
 # File generated from our OpenAPI spec by Stainless.
 
-from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse
-from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse
+from .chat import (
+    Chat,
+    AsyncChat,
+    ChatWithRawResponse,
+    AsyncChatWithRawResponse,
+    ChatWithStreamingResponse,
+    AsyncChatWithStreamingResponse,
+)
+from .completions import (
+    Completions,
+    AsyncCompletions,
+    CompletionsWithRawResponse,
+    AsyncCompletionsWithRawResponse,
+    CompletionsWithStreamingResponse,
+    AsyncCompletionsWithStreamingResponse,
+)
 
 __all__ = [
     "Completions",
     "AsyncCompletions",
     "CompletionsWithRawResponse",
     "AsyncCompletionsWithRawResponse",
+    "CompletionsWithStreamingResponse",
+    "AsyncCompletionsWithStreamingResponse",
     "Chat",
     "AsyncChat",
     "ChatWithRawResponse",
     "AsyncChatWithRawResponse",
+    "ChatWithStreamingResponse",
+    "AsyncChatWithStreamingResponse",
 ]
src/openai/resources/chat/chat.py
@@ -4,7 +4,14 @@ from __future__ import annotations
 
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
-from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse
+from .completions import (
+    Completions,
+    AsyncCompletions,
+    CompletionsWithRawResponse,
+    AsyncCompletionsWithRawResponse,
+    CompletionsWithStreamingResponse,
+    AsyncCompletionsWithStreamingResponse,
+)
 
 __all__ = ["Chat", "AsyncChat"]
 
@@ -18,6 +25,10 @@ class Chat(SyncAPIResource):
     def with_raw_response(self) -> ChatWithRawResponse:
         return ChatWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> ChatWithStreamingResponse:
+        return ChatWithStreamingResponse(self)
+
 
 class AsyncChat(AsyncAPIResource):
     @cached_property
@@ -28,6 +39,10 @@ class AsyncChat(AsyncAPIResource):
     def with_raw_response(self) -> AsyncChatWithRawResponse:
         return AsyncChatWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncChatWithStreamingResponse:
+        return AsyncChatWithStreamingResponse(self)
+
 
 class ChatWithRawResponse:
     def __init__(self, chat: Chat) -> None:
@@ -37,3 +52,13 @@ class ChatWithRawResponse:
 class AsyncChatWithRawResponse:
     def __init__(self, chat: AsyncChat) -> None:
         self.completions = AsyncCompletionsWithRawResponse(chat.completions)
+
+
+class ChatWithStreamingResponse:
+    def __init__(self, chat: Chat) -> None:
+        self.completions = CompletionsWithStreamingResponse(chat.completions)
+
+
+class AsyncChatWithStreamingResponse:
+    def __init__(self, chat: AsyncChat) -> None:
+        self.completions = AsyncCompletionsWithStreamingResponse(chat.completions)
src/openai/resources/chat/completions.py
@@ -7,11 +7,12 @@ from typing_extensions import Literal
 
 import httpx
 
+from ... import _legacy_response
 from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from ..._utils import required_args, maybe_transform
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from ..._streaming import Stream, AsyncStream
 from ...types.chat import (
     ChatCompletion,
@@ -33,6 +34,10 @@ class Completions(SyncAPIResource):
     def with_raw_response(self) -> CompletionsWithRawResponse:
         return CompletionsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> CompletionsWithStreamingResponse:
+        return CompletionsWithStreamingResponse(self)
+
     @overload
     def create(
         self,
@@ -681,6 +686,10 @@ class AsyncCompletions(AsyncAPIResource):
     def with_raw_response(self) -> AsyncCompletionsWithRawResponse:
         return AsyncCompletionsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse:
+        return AsyncCompletionsWithStreamingResponse(self)
+
     @overload
     async def create(
         self,
@@ -1326,13 +1335,27 @@ class AsyncCompletions(AsyncAPIResource):
 
 class CompletionsWithRawResponse:
     def __init__(self, completions: Completions) -> None:
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             completions.create,
         )
 
 
 class AsyncCompletionsWithRawResponse:
     def __init__(self, completions: AsyncCompletions) -> None:
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            completions.create,
+        )
+
+
+class CompletionsWithStreamingResponse:
+    def __init__(self, completions: Completions) -> None:
+        self.create = to_streamed_response_wrapper(
+            completions.create,
+        )
+
+
+class AsyncCompletionsWithStreamingResponse:
+    def __init__(self, completions: AsyncCompletions) -> None:
+        self.create = async_to_streamed_response_wrapper(
             completions.create,
         )
src/openai/resources/fine_tuning/__init__.py
@@ -1,15 +1,33 @@
 # File generated from our OpenAPI spec by Stainless.
 
-from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse
-from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse
+from .jobs import (
+    Jobs,
+    AsyncJobs,
+    JobsWithRawResponse,
+    AsyncJobsWithRawResponse,
+    JobsWithStreamingResponse,
+    AsyncJobsWithStreamingResponse,
+)
+from .fine_tuning import (
+    FineTuning,
+    AsyncFineTuning,
+    FineTuningWithRawResponse,
+    AsyncFineTuningWithRawResponse,
+    FineTuningWithStreamingResponse,
+    AsyncFineTuningWithStreamingResponse,
+)
 
 __all__ = [
     "Jobs",
     "AsyncJobs",
     "JobsWithRawResponse",
     "AsyncJobsWithRawResponse",
+    "JobsWithStreamingResponse",
+    "AsyncJobsWithStreamingResponse",
     "FineTuning",
     "AsyncFineTuning",
     "FineTuningWithRawResponse",
     "AsyncFineTuningWithRawResponse",
+    "FineTuningWithStreamingResponse",
+    "AsyncFineTuningWithStreamingResponse",
 ]
src/openai/resources/fine_tuning/fine_tuning.py
@@ -2,7 +2,14 @@
 
 from __future__ import annotations
 
-from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse
+from .jobs import (
+    Jobs,
+    AsyncJobs,
+    JobsWithRawResponse,
+    AsyncJobsWithRawResponse,
+    JobsWithStreamingResponse,
+    AsyncJobsWithStreamingResponse,
+)
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
 
@@ -18,6 +25,10 @@ class FineTuning(SyncAPIResource):
     def with_raw_response(self) -> FineTuningWithRawResponse:
         return FineTuningWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> FineTuningWithStreamingResponse:
+        return FineTuningWithStreamingResponse(self)
+
 
 class AsyncFineTuning(AsyncAPIResource):
     @cached_property
@@ -28,6 +39,10 @@ class AsyncFineTuning(AsyncAPIResource):
     def with_raw_response(self) -> AsyncFineTuningWithRawResponse:
         return AsyncFineTuningWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncFineTuningWithStreamingResponse:
+        return AsyncFineTuningWithStreamingResponse(self)
+
 
 class FineTuningWithRawResponse:
     def __init__(self, fine_tuning: FineTuning) -> None:
@@ -37,3 +52,13 @@ class FineTuningWithRawResponse:
 class AsyncFineTuningWithRawResponse:
     def __init__(self, fine_tuning: AsyncFineTuning) -> None:
         self.jobs = AsyncJobsWithRawResponse(fine_tuning.jobs)
+
+
+class FineTuningWithStreamingResponse:
+    def __init__(self, fine_tuning: FineTuning) -> None:
+        self.jobs = JobsWithStreamingResponse(fine_tuning.jobs)
+
+
+class AsyncFineTuningWithStreamingResponse:
+    def __init__(self, fine_tuning: AsyncFineTuning) -> None:
+        self.jobs = AsyncJobsWithStreamingResponse(fine_tuning.jobs)
src/openai/resources/fine_tuning/jobs.py
@@ -7,11 +7,12 @@ from typing_extensions import Literal
 
 import httpx
 
+from ... import _legacy_response
 from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from ..._utils import maybe_transform
 from ..._compat import cached_property
 from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from ...pagination import SyncCursorPage, AsyncCursorPage
 from ..._base_client import (
     AsyncPaginator,
@@ -33,6 +34,10 @@ class Jobs(SyncAPIResource):
     def with_raw_response(self) -> JobsWithRawResponse:
         return JobsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> JobsWithStreamingResponse:
+        return JobsWithStreamingResponse(self)
+
     def create(
         self,
         *,
@@ -284,6 +289,10 @@ class AsyncJobs(AsyncAPIResource):
     def with_raw_response(self) -> AsyncJobsWithRawResponse:
         return AsyncJobsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncJobsWithStreamingResponse:
+        return AsyncJobsWithStreamingResponse(self)
+
     async def create(
         self,
         *,
@@ -532,37 +541,75 @@ class AsyncJobs(AsyncAPIResource):
 
 class JobsWithRawResponse:
     def __init__(self, jobs: Jobs) -> None:
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             jobs.create,
         )
-        self.retrieve = to_raw_response_wrapper(
+        self.retrieve = _legacy_response.to_raw_response_wrapper(
             jobs.retrieve,
         )
-        self.list = to_raw_response_wrapper(
+        self.list = _legacy_response.to_raw_response_wrapper(
             jobs.list,
         )
-        self.cancel = to_raw_response_wrapper(
+        self.cancel = _legacy_response.to_raw_response_wrapper(
             jobs.cancel,
         )
-        self.list_events = to_raw_response_wrapper(
+        self.list_events = _legacy_response.to_raw_response_wrapper(
             jobs.list_events,
         )
 
 
 class AsyncJobsWithRawResponse:
     def __init__(self, jobs: AsyncJobs) -> None:
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            jobs.create,
+        )
+        self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+            jobs.retrieve,
+        )
+        self.list = _legacy_response.async_to_raw_response_wrapper(
+            jobs.list,
+        )
+        self.cancel = _legacy_response.async_to_raw_response_wrapper(
+            jobs.cancel,
+        )
+        self.list_events = _legacy_response.async_to_raw_response_wrapper(
+            jobs.list_events,
+        )
+
+
+class JobsWithStreamingResponse:
+    def __init__(self, jobs: Jobs) -> None:
+        self.create = to_streamed_response_wrapper(
+            jobs.create,
+        )
+        self.retrieve = to_streamed_response_wrapper(
+            jobs.retrieve,
+        )
+        self.list = to_streamed_response_wrapper(
+            jobs.list,
+        )
+        self.cancel = to_streamed_response_wrapper(
+            jobs.cancel,
+        )
+        self.list_events = to_streamed_response_wrapper(
+            jobs.list_events,
+        )
+
+
+class AsyncJobsWithStreamingResponse:
+    def __init__(self, jobs: AsyncJobs) -> None:
+        self.create = async_to_streamed_response_wrapper(
             jobs.create,
         )
-        self.retrieve = async_to_raw_response_wrapper(
+        self.retrieve = async_to_streamed_response_wrapper(
             jobs.retrieve,
         )
-        self.list = async_to_raw_response_wrapper(
+        self.list = async_to_streamed_response_wrapper(
             jobs.list,
         )
-        self.cancel = async_to_raw_response_wrapper(
+        self.cancel = async_to_streamed_response_wrapper(
             jobs.cancel,
         )
-        self.list_events = async_to_raw_response_wrapper(
+        self.list_events = async_to_streamed_response_wrapper(
             jobs.list_events,
         )
src/openai/resources/__init__.py
@@ -1,55 +1,145 @@
 # 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 .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse
-from .images import Images, AsyncImages, ImagesWithRawResponse, AsyncImagesWithRawResponse
-from .models import Models, AsyncModels, ModelsWithRawResponse, AsyncModelsWithRawResponse
-from .embeddings import Embeddings, AsyncEmbeddings, EmbeddingsWithRawResponse, AsyncEmbeddingsWithRawResponse
-from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse
-from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse
-from .moderations import Moderations, AsyncModerations, ModerationsWithRawResponse, AsyncModerationsWithRawResponse
+from .beta import (
+    Beta,
+    AsyncBeta,
+    BetaWithRawResponse,
+    AsyncBetaWithRawResponse,
+    BetaWithStreamingResponse,
+    AsyncBetaWithStreamingResponse,
+)
+from .chat import (
+    Chat,
+    AsyncChat,
+    ChatWithRawResponse,
+    AsyncChatWithRawResponse,
+    ChatWithStreamingResponse,
+    AsyncChatWithStreamingResponse,
+)
+from .audio import (
+    Audio,
+    AsyncAudio,
+    AudioWithRawResponse,
+    AsyncAudioWithRawResponse,
+    AudioWithStreamingResponse,
+    AsyncAudioWithStreamingResponse,
+)
+from .files import (
+    Files,
+    AsyncFiles,
+    FilesWithRawResponse,
+    AsyncFilesWithRawResponse,
+    FilesWithStreamingResponse,
+    AsyncFilesWithStreamingResponse,
+)
+from .images import (
+    Images,
+    AsyncImages,
+    ImagesWithRawResponse,
+    AsyncImagesWithRawResponse,
+    ImagesWithStreamingResponse,
+    AsyncImagesWithStreamingResponse,
+)
+from .models import (
+    Models,
+    AsyncModels,
+    ModelsWithRawResponse,
+    AsyncModelsWithRawResponse,
+    ModelsWithStreamingResponse,
+    AsyncModelsWithStreamingResponse,
+)
+from .embeddings import (
+    Embeddings,
+    AsyncEmbeddings,
+    EmbeddingsWithRawResponse,
+    AsyncEmbeddingsWithRawResponse,
+    EmbeddingsWithStreamingResponse,
+    AsyncEmbeddingsWithStreamingResponse,
+)
+from .completions import (
+    Completions,
+    AsyncCompletions,
+    CompletionsWithRawResponse,
+    AsyncCompletionsWithRawResponse,
+    CompletionsWithStreamingResponse,
+    AsyncCompletionsWithStreamingResponse,
+)
+from .fine_tuning import (
+    FineTuning,
+    AsyncFineTuning,
+    FineTuningWithRawResponse,
+    AsyncFineTuningWithRawResponse,
+    FineTuningWithStreamingResponse,
+    AsyncFineTuningWithStreamingResponse,
+)
+from .moderations import (
+    Moderations,
+    AsyncModerations,
+    ModerationsWithRawResponse,
+    AsyncModerationsWithRawResponse,
+    ModerationsWithStreamingResponse,
+    AsyncModerationsWithStreamingResponse,
+)
 
 __all__ = [
     "Completions",
     "AsyncCompletions",
     "CompletionsWithRawResponse",
     "AsyncCompletionsWithRawResponse",
+    "CompletionsWithStreamingResponse",
+    "AsyncCompletionsWithStreamingResponse",
     "Chat",
     "AsyncChat",
     "ChatWithRawResponse",
     "AsyncChatWithRawResponse",
+    "ChatWithStreamingResponse",
+    "AsyncChatWithStreamingResponse",
     "Embeddings",
     "AsyncEmbeddings",
     "EmbeddingsWithRawResponse",
     "AsyncEmbeddingsWithRawResponse",
+    "EmbeddingsWithStreamingResponse",
+    "AsyncEmbeddingsWithStreamingResponse",
     "Files",
     "AsyncFiles",
     "FilesWithRawResponse",
     "AsyncFilesWithRawResponse",
+    "FilesWithStreamingResponse",
+    "AsyncFilesWithStreamingResponse",
     "Images",
     "AsyncImages",
     "ImagesWithRawResponse",
     "AsyncImagesWithRawResponse",
+    "ImagesWithStreamingResponse",
+    "AsyncImagesWithStreamingResponse",
     "Audio",
     "AsyncAudio",
     "AudioWithRawResponse",
     "AsyncAudioWithRawResponse",
+    "AudioWithStreamingResponse",
+    "AsyncAudioWithStreamingResponse",
     "Moderations",
     "AsyncModerations",
     "ModerationsWithRawResponse",
     "AsyncModerationsWithRawResponse",
+    "ModerationsWithStreamingResponse",
+    "AsyncModerationsWithStreamingResponse",
     "Models",
     "AsyncModels",
     "ModelsWithRawResponse",
     "AsyncModelsWithRawResponse",
+    "ModelsWithStreamingResponse",
+    "AsyncModelsWithStreamingResponse",
     "FineTuning",
     "AsyncFineTuning",
     "FineTuningWithRawResponse",
     "AsyncFineTuningWithRawResponse",
+    "FineTuningWithStreamingResponse",
+    "AsyncFineTuningWithStreamingResponse",
     "Beta",
     "AsyncBeta",
     "BetaWithRawResponse",
     "AsyncBetaWithRawResponse",
+    "BetaWithStreamingResponse",
+    "AsyncBetaWithStreamingResponse",
 ]
src/openai/resources/completions.py
@@ -7,12 +7,13 @@ from typing_extensions import Literal
 
 import httpx
 
+from .. import _legacy_response
 from ..types import Completion, completion_create_params
 from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from .._utils import required_args, maybe_transform
 from .._compat import cached_property
 from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from .._streaming import Stream, AsyncStream
 from .._base_client import (
     make_request_options,
@@ -26,6 +27,10 @@ class Completions(SyncAPIResource):
     def with_raw_response(self) -> CompletionsWithRawResponse:
         return CompletionsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> CompletionsWithStreamingResponse:
+        return CompletionsWithStreamingResponse(self)
+
     @overload
     def create(
         self,
@@ -536,6 +541,10 @@ class AsyncCompletions(AsyncAPIResource):
     def with_raw_response(self) -> AsyncCompletionsWithRawResponse:
         return AsyncCompletionsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse:
+        return AsyncCompletionsWithStreamingResponse(self)
+
     @overload
     async def create(
         self,
@@ -1043,13 +1052,27 @@ class AsyncCompletions(AsyncAPIResource):
 
 class CompletionsWithRawResponse:
     def __init__(self, completions: Completions) -> None:
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             completions.create,
         )
 
 
 class AsyncCompletionsWithRawResponse:
     def __init__(self, completions: AsyncCompletions) -> None:
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            completions.create,
+        )
+
+
+class CompletionsWithStreamingResponse:
+    def __init__(self, completions: Completions) -> None:
+        self.create = to_streamed_response_wrapper(
+            completions.create,
+        )
+
+
+class AsyncCompletionsWithStreamingResponse:
+    def __init__(self, completions: AsyncCompletions) -> None:
+        self.create = async_to_streamed_response_wrapper(
             completions.create,
         )
src/openai/resources/embeddings.py
@@ -8,13 +8,14 @@ from typing_extensions import Literal
 
 import httpx
 
+from .. import _legacy_response
 from ..types import CreateEmbeddingResponse, embedding_create_params
 from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from .._utils import is_given, maybe_transform
 from .._compat import cached_property
 from .._extras import numpy as np, has_numpy
 from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from .._base_client import (
     make_request_options,
 )
@@ -27,6 +28,10 @@ class Embeddings(SyncAPIResource):
     def with_raw_response(self) -> EmbeddingsWithRawResponse:
         return EmbeddingsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> EmbeddingsWithStreamingResponse:
+        return EmbeddingsWithStreamingResponse(self)
+
     def create(
         self,
         *,
@@ -119,6 +124,10 @@ class AsyncEmbeddings(AsyncAPIResource):
     def with_raw_response(self) -> AsyncEmbeddingsWithRawResponse:
         return AsyncEmbeddingsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncEmbeddingsWithStreamingResponse:
+        return AsyncEmbeddingsWithStreamingResponse(self)
+
     async def create(
         self,
         *,
@@ -208,13 +217,27 @@ class AsyncEmbeddings(AsyncAPIResource):
 
 class EmbeddingsWithRawResponse:
     def __init__(self, embeddings: Embeddings) -> None:
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             embeddings.create,
         )
 
 
 class AsyncEmbeddingsWithRawResponse:
     def __init__(self, embeddings: AsyncEmbeddings) -> None:
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            embeddings.create,
+        )
+
+
+class EmbeddingsWithStreamingResponse:
+    def __init__(self, embeddings: Embeddings) -> None:
+        self.create = to_streamed_response_wrapper(
+            embeddings.create,
+        )
+
+
+class AsyncEmbeddingsWithStreamingResponse:
+    def __init__(self, embeddings: AsyncEmbeddings) -> None:
+        self.create = async_to_streamed_response_wrapper(
             embeddings.create,
         )
src/openai/resources/files.py
@@ -9,16 +9,23 @@ from typing_extensions import Literal
 
 import httpx
 
+from .. import _legacy_response
 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 .._compat import cached_property
 from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from .._response import (
+    StreamedBinaryAPIResponse,
+    AsyncStreamedBinaryAPIResponse,
+    to_streamed_response_wrapper,
+    async_to_streamed_response_wrapper,
+    to_custom_streamed_response_wrapper,
+    async_to_custom_streamed_response_wrapper,
+)
 from ..pagination import SyncPage, AsyncPage
 from .._base_client import (
     AsyncPaginator,
-    HttpxBinaryResponseContent,
     make_request_options,
 )
 
@@ -30,6 +37,10 @@ class Files(SyncAPIResource):
     def with_raw_response(self) -> FilesWithRawResponse:
         return FilesWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> FilesWithStreamingResponse:
+        return FilesWithStreamingResponse(self)
+
     def create(
         self,
         *,
@@ -209,7 +220,7 @@ class Files(SyncAPIResource):
         extra_query: Query | None = None,
         extra_body: Body | None = None,
         timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
-    ) -> HttpxBinaryResponseContent:
+    ) -> _legacy_response.HttpxBinaryResponseContent:
         """
         Returns the contents of the specified file.
 
@@ -227,7 +238,7 @@ class Files(SyncAPIResource):
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
-            cast_to=HttpxBinaryResponseContent,
+            cast_to=_legacy_response.HttpxBinaryResponseContent,
         )
 
     @typing_extensions.deprecated("The `.content()` method should be used instead")
@@ -292,6 +303,10 @@ class AsyncFiles(AsyncAPIResource):
     def with_raw_response(self) -> AsyncFilesWithRawResponse:
         return AsyncFilesWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncFilesWithStreamingResponse:
+        return AsyncFilesWithStreamingResponse(self)
+
     async def create(
         self,
         *,
@@ -471,7 +486,7 @@ class AsyncFiles(AsyncAPIResource):
         extra_query: Query | None = None,
         extra_body: Body | None = None,
         timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
-    ) -> HttpxBinaryResponseContent:
+    ) -> _legacy_response.HttpxBinaryResponseContent:
         """
         Returns the contents of the specified file.
 
@@ -489,7 +504,7 @@ class AsyncFiles(AsyncAPIResource):
             options=make_request_options(
                 extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
             ),
-            cast_to=HttpxBinaryResponseContent,
+            cast_to=_legacy_response.HttpxBinaryResponseContent,
         )
 
     @typing_extensions.deprecated("The `.content()` method should be used instead")
@@ -551,43 +566,97 @@ class AsyncFiles(AsyncAPIResource):
 
 class FilesWithRawResponse:
     def __init__(self, files: Files) -> None:
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             files.create,
         )
-        self.retrieve = to_raw_response_wrapper(
+        self.retrieve = _legacy_response.to_raw_response_wrapper(
             files.retrieve,
         )
-        self.list = to_raw_response_wrapper(
+        self.list = _legacy_response.to_raw_response_wrapper(
             files.list,
         )
-        self.delete = to_raw_response_wrapper(
+        self.delete = _legacy_response.to_raw_response_wrapper(
             files.delete,
         )
-        self.content = to_raw_response_wrapper(
+        self.content = _legacy_response.to_raw_response_wrapper(
             files.content,
         )
-        self.retrieve_content = to_raw_response_wrapper(  # pyright: ignore[reportDeprecated]
-            files.retrieve_content  # pyright: ignore[reportDeprecated],
+        self.retrieve_content = (  # pyright: ignore[reportDeprecated]
+            _legacy_response.to_raw_response_wrapper(
+                files.retrieve_content  # pyright: ignore[reportDeprecated],
+            )
         )
 
 
 class AsyncFilesWithRawResponse:
     def __init__(self, files: AsyncFiles) -> None:
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            files.create,
+        )
+        self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+            files.retrieve,
+        )
+        self.list = _legacy_response.async_to_raw_response_wrapper(
+            files.list,
+        )
+        self.delete = _legacy_response.async_to_raw_response_wrapper(
+            files.delete,
+        )
+        self.content = _legacy_response.async_to_raw_response_wrapper(
+            files.content,
+        )
+        self.retrieve_content = (  # pyright: ignore[reportDeprecated]
+            _legacy_response.async_to_raw_response_wrapper(
+                files.retrieve_content  # pyright: ignore[reportDeprecated],
+            )
+        )
+
+
+class FilesWithStreamingResponse:
+    def __init__(self, files: Files) -> None:
+        self.create = to_streamed_response_wrapper(
+            files.create,
+        )
+        self.retrieve = to_streamed_response_wrapper(
+            files.retrieve,
+        )
+        self.list = to_streamed_response_wrapper(
+            files.list,
+        )
+        self.delete = to_streamed_response_wrapper(
+            files.delete,
+        )
+        self.content = to_custom_streamed_response_wrapper(
+            files.content,
+            StreamedBinaryAPIResponse,
+        )
+        self.retrieve_content = (  # pyright: ignore[reportDeprecated]
+            to_streamed_response_wrapper(
+                files.retrieve_content  # pyright: ignore[reportDeprecated],
+            )
+        )
+
+
+class AsyncFilesWithStreamingResponse:
+    def __init__(self, files: AsyncFiles) -> None:
+        self.create = async_to_streamed_response_wrapper(
             files.create,
         )
-        self.retrieve = async_to_raw_response_wrapper(
+        self.retrieve = async_to_streamed_response_wrapper(
             files.retrieve,
         )
-        self.list = async_to_raw_response_wrapper(
+        self.list = async_to_streamed_response_wrapper(
             files.list,
         )
-        self.delete = async_to_raw_response_wrapper(
+        self.delete = async_to_streamed_response_wrapper(
             files.delete,
         )
-        self.content = async_to_raw_response_wrapper(
+        self.content = async_to_custom_streamed_response_wrapper(
             files.content,
+            AsyncStreamedBinaryAPIResponse,
         )
-        self.retrieve_content = async_to_raw_response_wrapper(  # pyright: ignore[reportDeprecated]
-            files.retrieve_content  # pyright: ignore[reportDeprecated],
+        self.retrieve_content = (  # pyright: ignore[reportDeprecated]
+            async_to_streamed_response_wrapper(
+                files.retrieve_content  # pyright: ignore[reportDeprecated],
+            )
         )
src/openai/resources/images.py
@@ -7,6 +7,7 @@ from typing_extensions import Literal
 
 import httpx
 
+from .. import _legacy_response
 from ..types import (
     ImagesResponse,
     image_edit_params,
@@ -17,7 +18,7 @@ from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes
 from .._utils import extract_files, maybe_transform, deepcopy_minimal
 from .._compat import cached_property
 from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from .._base_client import (
     make_request_options,
 )
@@ -30,6 +31,10 @@ class Images(SyncAPIResource):
     def with_raw_response(self) -> ImagesWithRawResponse:
         return ImagesWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> ImagesWithStreamingResponse:
+        return ImagesWithStreamingResponse(self)
+
     def create_variation(
         self,
         *,
@@ -273,6 +278,10 @@ class AsyncImages(AsyncAPIResource):
     def with_raw_response(self) -> AsyncImagesWithRawResponse:
         return AsyncImagesWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncImagesWithStreamingResponse:
+        return AsyncImagesWithStreamingResponse(self)
+
     async def create_variation(
         self,
         *,
@@ -513,25 +522,51 @@ class AsyncImages(AsyncAPIResource):
 
 class ImagesWithRawResponse:
     def __init__(self, images: Images) -> None:
-        self.create_variation = to_raw_response_wrapper(
+        self.create_variation = _legacy_response.to_raw_response_wrapper(
             images.create_variation,
         )
-        self.edit = to_raw_response_wrapper(
+        self.edit = _legacy_response.to_raw_response_wrapper(
             images.edit,
         )
-        self.generate = to_raw_response_wrapper(
+        self.generate = _legacy_response.to_raw_response_wrapper(
             images.generate,
         )
 
 
 class AsyncImagesWithRawResponse:
     def __init__(self, images: AsyncImages) -> None:
-        self.create_variation = async_to_raw_response_wrapper(
+        self.create_variation = _legacy_response.async_to_raw_response_wrapper(
+            images.create_variation,
+        )
+        self.edit = _legacy_response.async_to_raw_response_wrapper(
+            images.edit,
+        )
+        self.generate = _legacy_response.async_to_raw_response_wrapper(
+            images.generate,
+        )
+
+
+class ImagesWithStreamingResponse:
+    def __init__(self, images: Images) -> None:
+        self.create_variation = to_streamed_response_wrapper(
+            images.create_variation,
+        )
+        self.edit = to_streamed_response_wrapper(
+            images.edit,
+        )
+        self.generate = to_streamed_response_wrapper(
+            images.generate,
+        )
+
+
+class AsyncImagesWithStreamingResponse:
+    def __init__(self, images: AsyncImages) -> None:
+        self.create_variation = async_to_streamed_response_wrapper(
             images.create_variation,
         )
-        self.edit = async_to_raw_response_wrapper(
+        self.edit = async_to_streamed_response_wrapper(
             images.edit,
         )
-        self.generate = async_to_raw_response_wrapper(
+        self.generate = async_to_streamed_response_wrapper(
             images.generate,
         )
src/openai/resources/models.py
@@ -4,11 +4,12 @@ from __future__ import annotations
 
 import httpx
 
+from .. import _legacy_response
 from ..types import Model, ModelDeleted
 from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from .._compat import cached_property
 from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from ..pagination import SyncPage, AsyncPage
 from .._base_client import (
     AsyncPaginator,
@@ -23,6 +24,10 @@ class Models(SyncAPIResource):
     def with_raw_response(self) -> ModelsWithRawResponse:
         return ModelsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> ModelsWithStreamingResponse:
+        return ModelsWithStreamingResponse(self)
+
     def retrieve(
         self,
         model: str,
@@ -117,6 +122,10 @@ class AsyncModels(AsyncAPIResource):
     def with_raw_response(self) -> AsyncModelsWithRawResponse:
         return AsyncModelsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncModelsWithStreamingResponse:
+        return AsyncModelsWithStreamingResponse(self)
+
     async def retrieve(
         self,
         model: str,
@@ -208,25 +217,51 @@ class AsyncModels(AsyncAPIResource):
 
 class ModelsWithRawResponse:
     def __init__(self, models: Models) -> None:
-        self.retrieve = to_raw_response_wrapper(
+        self.retrieve = _legacy_response.to_raw_response_wrapper(
             models.retrieve,
         )
-        self.list = to_raw_response_wrapper(
+        self.list = _legacy_response.to_raw_response_wrapper(
             models.list,
         )
-        self.delete = to_raw_response_wrapper(
+        self.delete = _legacy_response.to_raw_response_wrapper(
             models.delete,
         )
 
 
 class AsyncModelsWithRawResponse:
     def __init__(self, models: AsyncModels) -> None:
-        self.retrieve = async_to_raw_response_wrapper(
+        self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+            models.retrieve,
+        )
+        self.list = _legacy_response.async_to_raw_response_wrapper(
+            models.list,
+        )
+        self.delete = _legacy_response.async_to_raw_response_wrapper(
+            models.delete,
+        )
+
+
+class ModelsWithStreamingResponse:
+    def __init__(self, models: Models) -> None:
+        self.retrieve = to_streamed_response_wrapper(
+            models.retrieve,
+        )
+        self.list = to_streamed_response_wrapper(
+            models.list,
+        )
+        self.delete = to_streamed_response_wrapper(
+            models.delete,
+        )
+
+
+class AsyncModelsWithStreamingResponse:
+    def __init__(self, models: AsyncModels) -> None:
+        self.retrieve = async_to_streamed_response_wrapper(
             models.retrieve,
         )
-        self.list = async_to_raw_response_wrapper(
+        self.list = async_to_streamed_response_wrapper(
             models.list,
         )
-        self.delete = async_to_raw_response_wrapper(
+        self.delete = async_to_streamed_response_wrapper(
             models.delete,
         )
src/openai/resources/moderations.py
@@ -7,12 +7,13 @@ from typing_extensions import Literal
 
 import httpx
 
+from .. import _legacy_response
 from ..types import ModerationCreateResponse, moderation_create_params
 from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
 from .._utils import maybe_transform
 from .._compat import cached_property
 from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
 from .._base_client import (
     make_request_options,
 )
@@ -25,6 +26,10 @@ class Moderations(SyncAPIResource):
     def with_raw_response(self) -> ModerationsWithRawResponse:
         return ModerationsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> ModerationsWithStreamingResponse:
+        return ModerationsWithStreamingResponse(self)
+
     def create(
         self,
         *,
@@ -81,6 +86,10 @@ class AsyncModerations(AsyncAPIResource):
     def with_raw_response(self) -> AsyncModerationsWithRawResponse:
         return AsyncModerationsWithRawResponse(self)
 
+    @cached_property
+    def with_streaming_response(self) -> AsyncModerationsWithStreamingResponse:
+        return AsyncModerationsWithStreamingResponse(self)
+
     async def create(
         self,
         *,
@@ -134,13 +143,27 @@ class AsyncModerations(AsyncAPIResource):
 
 class ModerationsWithRawResponse:
     def __init__(self, moderations: Moderations) -> None:
-        self.create = to_raw_response_wrapper(
+        self.create = _legacy_response.to_raw_response_wrapper(
             moderations.create,
         )
 
 
 class AsyncModerationsWithRawResponse:
     def __init__(self, moderations: AsyncModerations) -> None:
-        self.create = async_to_raw_response_wrapper(
+        self.create = _legacy_response.async_to_raw_response_wrapper(
+            moderations.create,
+        )
+
+
+class ModerationsWithStreamingResponse:
+    def __init__(self, moderations: Moderations) -> None:
+        self.create = to_streamed_response_wrapper(
+            moderations.create,
+        )
+
+
+class AsyncModerationsWithStreamingResponse:
+    def __init__(self, moderations: AsyncModerations) -> None:
+        self.create = async_to_streamed_response_wrapper(
             moderations.create,
         )
src/openai/__init__.py
@@ -10,6 +10,7 @@ from ._types import NoneType, Transport, ProxiesTypes
 from ._utils import file_from_path
 from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions
 from ._version import __title__, __version__
+from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse
 from ._exceptions import (
     APIError,
     OpenAIError,
src/openai/_base_client.py
@@ -1,6 +1,5 @@
 from __future__ import annotations
 
-import os
 import json
 import time
 import uuid
@@ -31,7 +30,7 @@ from typing import (
     overload,
 )
 from functools import lru_cache
-from typing_extensions import Literal, override
+from typing_extensions import Literal, override, get_origin
 
 import anyio
 import httpx
@@ -61,18 +60,22 @@ from ._types import (
     AsyncTransport,
     RequestOptions,
     ModelBuilderProtocol,
-    BinaryResponseContent,
 )
 from ._utils import is_dict, is_given, is_mapping
 from ._compat import model_copy, model_dump
 from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type
-from ._response import APIResponse
+from ._response import (
+    APIResponse,
+    BaseAPIResponse,
+    AsyncAPIResponse,
+    extract_response_type,
+)
 from ._constants import (
     DEFAULT_LIMITS,
     DEFAULT_TIMEOUT,
     DEFAULT_MAX_RETRIES,
     RAW_RESPONSE_HEADER,
-    STREAMED_RAW_RESPONSE_HEADER,
+    OVERRIDE_CAST_TO_HEADER,
 )
 from ._streaming import Stream, AsyncStream
 from ._exceptions import (
@@ -81,6 +84,7 @@ from ._exceptions import (
     APIConnectionError,
     APIResponseValidationError,
 )
+from ._legacy_response import LegacyAPIResponse
 
 log: logging.Logger = logging.getLogger(__name__)
 
@@ -493,28 +497,25 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
             serialized[key] = value
         return serialized
 
-    def _process_response(
-        self,
-        *,
-        cast_to: Type[ResponseT],
-        options: FinalRequestOptions,
-        response: httpx.Response,
-        stream: bool,
-        stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None,
-    ) -> ResponseT:
-        api_response = APIResponse(
-            raw=response,
-            client=self,
-            cast_to=cast_to,
-            stream=stream,
-            stream_cls=stream_cls,
-            options=options,
-        )
+    def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]:
+        if not is_given(options.headers):
+            return cast_to
 
-        if response.request.headers.get(RAW_RESPONSE_HEADER) == "true":
-            return cast(ResponseT, api_response)
+        # make a copy of the headers so we don't mutate user-input
+        headers = dict(options.headers)
 
-        return api_response.parse()
+        # we internally support defining a temporary header to override the
+        # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response`
+        # see _response.py for implementation details
+        override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN)
+        if is_given(override_cast_to):
+            options.headers = headers
+            return cast(Type[ResponseT], override_cast_to)
+
+        return cast_to
+
+    def _should_stream_response_body(self, request: httpx.Request) -> bool:
+        return request.headers.get(RAW_RESPONSE_HEADER) == "stream"  # type: ignore[no-any-return]
 
     def _process_response_data(
         self,
@@ -540,12 +541,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
         except pydantic.ValidationError as err:
             raise APIResponseValidationError(response=response, body=data) from err
 
-    def _should_stream_response_body(self, *, request: httpx.Request) -> bool:
-        if request.headers.get(STREAMED_RAW_RESPONSE_HEADER) == "true":
-            return True
-
-        return False
-
     @property
     def qs(self) -> Querystring:
         return Querystring()
@@ -610,6 +605,8 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
             if response_headers is not None:
                 retry_header = response_headers.get("retry-after")
                 try:
+                    # note: the spec indicates that this should only ever be an integer
+                    # but if someone sends a float there's no reason for us to not respect it
                     retry_after = float(retry_header)
                 except Exception:
                     retry_date_tuple = email.utils.parsedate_tz(retry_header)
@@ -873,6 +870,7 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
         stream: bool,
         stream_cls: type[_StreamT] | None,
     ) -> ResponseT | _StreamT:
+        cast_to = self._maybe_override_cast_to(cast_to, options)
         self._prepare_options(options)
 
         retries = self._remaining_retries(remaining_retries, options)
@@ -987,6 +985,63 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
             stream_cls=stream_cls,
         )
 
+    def _process_response(
+        self,
+        *,
+        cast_to: Type[ResponseT],
+        options: FinalRequestOptions,
+        response: httpx.Response,
+        stream: bool,
+        stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None,
+    ) -> ResponseT:
+        if response.request.headers.get(RAW_RESPONSE_HEADER) == "true":
+            return cast(
+                ResponseT,
+                LegacyAPIResponse(
+                    raw=response,
+                    client=self,
+                    cast_to=cast_to,
+                    stream=stream,
+                    stream_cls=stream_cls,
+                    options=options,
+                ),
+            )
+
+        origin = get_origin(cast_to) or cast_to
+
+        if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):
+            if not issubclass(origin, APIResponse):
+                raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}")
+
+            response_cls = cast("type[BaseAPIResponse[Any]]", cast_to)
+            return cast(
+                ResponseT,
+                response_cls(
+                    raw=response,
+                    client=self,
+                    cast_to=extract_response_type(response_cls),
+                    stream=stream,
+                    stream_cls=stream_cls,
+                    options=options,
+                ),
+            )
+
+        if cast_to == httpx.Response:
+            return cast(ResponseT, response)
+
+        api_response = APIResponse(
+            raw=response,
+            client=self,
+            cast_to=cast("type[ResponseT]", cast_to),  # pyright: ignore[reportUnnecessaryCast]
+            stream=stream,
+            stream_cls=stream_cls,
+            options=options,
+        )
+        if bool(response.request.headers.get(RAW_RESPONSE_HEADER)):
+            return cast(ResponseT, api_response)
+
+        return api_response.parse()
+
     def _request_api_list(
         self,
         model: Type[object],
@@ -1353,6 +1408,7 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
         stream_cls: type[_AsyncStreamT] | None,
         remaining_retries: int | None,
     ) -> ResponseT | _AsyncStreamT:
+        cast_to = self._maybe_override_cast_to(cast_to, options)
         await self._prepare_options(options)
 
         retries = self._remaining_retries(remaining_retries, options)
@@ -1428,7 +1484,7 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
             log.debug("Re-raising status error")
             raise self._make_status_error_from_response(err.response) from None
 
-        return self._process_response(
+        return await self._process_response(
             cast_to=cast_to,
             options=options,
             response=response,
@@ -1465,6 +1521,63 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
             stream_cls=stream_cls,
         )
 
+    async def _process_response(
+        self,
+        *,
+        cast_to: Type[ResponseT],
+        options: FinalRequestOptions,
+        response: httpx.Response,
+        stream: bool,
+        stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None,
+    ) -> ResponseT:
+        if response.request.headers.get(RAW_RESPONSE_HEADER) == "true":
+            return cast(
+                ResponseT,
+                LegacyAPIResponse(
+                    raw=response,
+                    client=self,
+                    cast_to=cast_to,
+                    stream=stream,
+                    stream_cls=stream_cls,
+                    options=options,
+                ),
+            )
+
+        origin = get_origin(cast_to) or cast_to
+
+        if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):
+            if not issubclass(origin, AsyncAPIResponse):
+                raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}")
+
+            response_cls = cast("type[BaseAPIResponse[Any]]", cast_to)
+            return cast(
+                "ResponseT",
+                response_cls(
+                    raw=response,
+                    client=self,
+                    cast_to=extract_response_type(response_cls),
+                    stream=stream,
+                    stream_cls=stream_cls,
+                    options=options,
+                ),
+            )
+
+        if cast_to == httpx.Response:
+            return cast(ResponseT, response)
+
+        api_response = AsyncAPIResponse(
+            raw=response,
+            client=self,
+            cast_to=cast("type[ResponseT]", cast_to),  # pyright: ignore[reportUnnecessaryCast]
+            stream=stream,
+            stream_cls=stream_cls,
+            options=options,
+        )
+        if bool(response.request.headers.get(RAW_RESPONSE_HEADER)):
+            return cast(ResponseT, api_response)
+
+        return await api_response.parse()
+
     def _request_api_list(
         self,
         model: Type[_T],
@@ -1783,105 +1896,3 @@ def _merge_mappings(
     """
     merged = {**obj1, **obj2}
     return {key: value for key, value in merged.items() if not isinstance(value, Omit)}
-
-
-class HttpxBinaryResponseContent(BinaryResponseContent):
-    response: httpx.Response
-
-    def __init__(self, response: httpx.Response) -> None:
-        self.response = response
-
-    @property
-    @override
-    def content(self) -> bytes:
-        return self.response.content
-
-    @property
-    @override
-    def text(self) -> str:
-        return self.response.text
-
-    @property
-    @override
-    def encoding(self) -> Optional[str]:
-        return self.response.encoding
-
-    @property
-    @override
-    def charset_encoding(self) -> Optional[str]:
-        return self.response.charset_encoding
-
-    @override
-    def json(self, **kwargs: Any) -> Any:
-        return self.response.json(**kwargs)
-
-    @override
-    def read(self) -> bytes:
-        return self.response.read()
-
-    @override
-    def iter_bytes(self, chunk_size: Optional[int] = None) -> Iterator[bytes]:
-        return self.response.iter_bytes(chunk_size)
-
-    @override
-    def iter_text(self, chunk_size: Optional[int] = None) -> Iterator[str]:
-        return self.response.iter_text(chunk_size)
-
-    @override
-    def iter_lines(self) -> Iterator[str]:
-        return self.response.iter_lines()
-
-    @override
-    def iter_raw(self, chunk_size: Optional[int] = None) -> Iterator[bytes]:
-        return self.response.iter_raw(chunk_size)
-
-    @override
-    def stream_to_file(
-        self,
-        file: str | os.PathLike[str],
-        *,
-        chunk_size: int | None = None,
-    ) -> None:
-        with open(file, mode="wb") as f:
-            for data in self.response.iter_bytes(chunk_size):
-                f.write(data)
-
-    @override
-    def close(self) -> None:
-        return self.response.close()
-
-    @override
-    async def aread(self) -> bytes:
-        return await self.response.aread()
-
-    @override
-    async def aiter_bytes(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]:
-        return self.response.aiter_bytes(chunk_size)
-
-    @override
-    async def aiter_text(self, chunk_size: Optional[int] = None) -> AsyncIterator[str]:
-        return self.response.aiter_text(chunk_size)
-
-    @override
-    async def aiter_lines(self) -> AsyncIterator[str]:
-        return self.response.aiter_lines()
-
-    @override
-    async def aiter_raw(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]:
-        return self.response.aiter_raw(chunk_size)
-
-    @override
-    async def astream_to_file(
-        self,
-        file: str | os.PathLike[str],
-        *,
-        chunk_size: int | None = None,
-    ) -> None:
-        path = anyio.Path(file)
-        async with await path.open(mode="wb") as f:
-            async for data in self.response.aiter_bytes(chunk_size):
-                await f.write(data)
-
-    @override
-    async def aclose(self) -> None:
-        return await self.response.aclose()
src/openai/_client.py
@@ -58,6 +58,7 @@ class OpenAI(SyncAPIClient):
     fine_tuning: resources.FineTuning
     beta: resources.Beta
     with_raw_response: OpenAIWithRawResponse
+    with_streaming_response: OpenAIWithStreamedResponse
 
     # client options
     api_key: str
@@ -132,6 +133,7 @@ class OpenAI(SyncAPIClient):
         self.fine_tuning = resources.FineTuning(self)
         self.beta = resources.Beta(self)
         self.with_raw_response = OpenAIWithRawResponse(self)
+        self.with_streaming_response = OpenAIWithStreamedResponse(self)
 
     @property
     @override
@@ -254,6 +256,7 @@ class AsyncOpenAI(AsyncAPIClient):
     fine_tuning: resources.AsyncFineTuning
     beta: resources.AsyncBeta
     with_raw_response: AsyncOpenAIWithRawResponse
+    with_streaming_response: AsyncOpenAIWithStreamedResponse
 
     # client options
     api_key: str
@@ -328,6 +331,7 @@ class AsyncOpenAI(AsyncAPIClient):
         self.fine_tuning = resources.AsyncFineTuning(self)
         self.beta = resources.AsyncBeta(self)
         self.with_raw_response = AsyncOpenAIWithRawResponse(self)
+        self.with_streaming_response = AsyncOpenAIWithStreamedResponse(self)
 
     @property
     @override
@@ -466,6 +470,34 @@ class AsyncOpenAIWithRawResponse:
         self.beta = resources.AsyncBetaWithRawResponse(client.beta)
 
 
+class OpenAIWithStreamedResponse:
+    def __init__(self, client: OpenAI) -> None:
+        self.completions = resources.CompletionsWithStreamingResponse(client.completions)
+        self.chat = resources.ChatWithStreamingResponse(client.chat)
+        self.embeddings = resources.EmbeddingsWithStreamingResponse(client.embeddings)
+        self.files = resources.FilesWithStreamingResponse(client.files)
+        self.images = resources.ImagesWithStreamingResponse(client.images)
+        self.audio = resources.AudioWithStreamingResponse(client.audio)
+        self.moderations = resources.ModerationsWithStreamingResponse(client.moderations)
+        self.models = resources.ModelsWithStreamingResponse(client.models)
+        self.fine_tuning = resources.FineTuningWithStreamingResponse(client.fine_tuning)
+        self.beta = resources.BetaWithStreamingResponse(client.beta)
+
+
+class AsyncOpenAIWithStreamedResponse:
+    def __init__(self, client: AsyncOpenAI) -> None:
+        self.completions = resources.AsyncCompletionsWithStreamingResponse(client.completions)
+        self.chat = resources.AsyncChatWithStreamingResponse(client.chat)
+        self.embeddings = resources.AsyncEmbeddingsWithStreamingResponse(client.embeddings)
+        self.files = resources.AsyncFilesWithStreamingResponse(client.files)
+        self.images = resources.AsyncImagesWithStreamingResponse(client.images)
+        self.audio = resources.AsyncAudioWithStreamingResponse(client.audio)
+        self.moderations = resources.AsyncModerationsWithStreamingResponse(client.moderations)
+        self.models = resources.AsyncModelsWithStreamingResponse(client.models)
+        self.fine_tuning = resources.AsyncFineTuningWithStreamingResponse(client.fine_tuning)
+        self.beta = resources.AsyncBetaWithStreamingResponse(client.beta)
+
+
 Client = OpenAI
 
 AsyncClient = AsyncOpenAI
src/openai/_constants.py
@@ -3,7 +3,7 @@
 import httpx
 
 RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response"
-STREAMED_RAW_RESPONSE_HEADER = "X-Stainless-Streamed-Raw-Response"
+OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to"
 
 # default timeout is 10 minutes
 DEFAULT_TIMEOUT = httpx.Timeout(timeout=600.0, connect=5.0)
src/openai/_legacy_response.py
@@ -0,0 +1,385 @@
+from __future__ import annotations
+
+import os
+import inspect
+import logging
+import datetime
+import functools
+from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, Iterator, AsyncIterator, cast
+from typing_extensions import Awaitable, ParamSpec, get_args, override, deprecated, get_origin
+
+import anyio
+import httpx
+
+from ._types import NoneType
+from ._utils import is_given
+from ._models import BaseModel, is_basemodel
+from ._constants import RAW_RESPONSE_HEADER
+from ._exceptions import APIResponseValidationError
+
+if TYPE_CHECKING:
+    from ._models import FinalRequestOptions
+    from ._base_client import Stream, BaseClient, AsyncStream
+
+
+P = ParamSpec("P")
+R = TypeVar("R")
+
+log: logging.Logger = logging.getLogger(__name__)
+
+
+class LegacyAPIResponse(Generic[R]):
+    """This is a legacy class as it will be replaced by `APIResponse`
+    and `AsyncAPIResponse` in the `_response.py` file in the next major
+    release.
+
+    For the sync client this will mostly be the same with the exception
+    of `content` & `text` will be methods instead of properties. In the
+    async client, all methods will be async.
+
+    A migration script will be provided & the migration in general should
+    be smooth.
+    """
+
+    _cast_to: type[R]
+    _client: BaseClient[Any, Any]
+    _parsed: R | None
+    _stream: bool
+    _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None
+    _options: FinalRequestOptions
+
+    http_response: httpx.Response
+
+    def __init__(
+        self,
+        *,
+        raw: httpx.Response,
+        cast_to: type[R],
+        client: BaseClient[Any, Any],
+        stream: bool,
+        stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None,
+        options: FinalRequestOptions,
+    ) -> None:
+        self._cast_to = cast_to
+        self._client = client
+        self._parsed = None
+        self._stream = stream
+        self._stream_cls = stream_cls
+        self._options = options
+        self.http_response = raw
+
+    def parse(self) -> R:
+        """Returns the rich python representation of this response's data.
+
+        For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`.
+
+        NOTE: For the async client: this will become a coroutine in the next major version.
+        """
+        if self._parsed is not None:
+            return self._parsed
+
+        parsed = self._parse()
+        if is_given(self._options.post_parser):
+            parsed = self._options.post_parser(parsed)
+
+        self._parsed = parsed
+        return parsed
+
+    @property
+    def headers(self) -> httpx.Headers:
+        return self.http_response.headers
+
+    @property
+    def http_request(self) -> httpx.Request:
+        return self.http_response.request
+
+    @property
+    def status_code(self) -> int:
+        return self.http_response.status_code
+
+    @property
+    def url(self) -> httpx.URL:
+        return self.http_response.url
+
+    @property
+    def method(self) -> str:
+        return self.http_request.method
+
+    @property
+    def content(self) -> bytes:
+        """Return the binary response content.
+
+        NOTE: this will be removed in favour of `.read()` in the
+        next major version.
+        """
+        return self.http_response.content
+
+    @property
+    def text(self) -> str:
+        """Return the decoded response content.
+
+        NOTE: this will be turned into a method in the next major version.
+        """
+        return self.http_response.text
+
+    @property
+    def http_version(self) -> str:
+        return self.http_response.http_version
+
+    @property
+    def is_closed(self) -> bool:
+        return self.http_response.is_closed
+
+    @property
+    def elapsed(self) -> datetime.timedelta:
+        """The time taken for the complete request/response cycle to complete."""
+        return self.http_response.elapsed
+
+    def _parse(self) -> R:
+        if self._stream:
+            if self._stream_cls:
+                return cast(
+                    R,
+                    self._stream_cls(
+                        cast_to=_extract_stream_chunk_type(self._stream_cls),
+                        response=self.http_response,
+                        client=cast(Any, self._client),
+                    ),
+                )
+
+            stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls)
+            if stream_cls is None:
+                raise MissingStreamClassError()
+
+            return cast(
+                R,
+                stream_cls(
+                    cast_to=self._cast_to,
+                    response=self.http_response,
+                    client=cast(Any, self._client),
+                ),
+            )
+
+        cast_to = self._cast_to
+        if cast_to is NoneType:
+            return cast(R, None)
+
+        response = self.http_response
+        if cast_to == str:
+            return cast(R, response.text)
+
+        origin = get_origin(cast_to) or cast_to
+
+        if inspect.isclass(origin) and issubclass(origin, HttpxBinaryResponseContent):
+            return cast(R, cast_to(response))  # type: ignore
+
+        if origin == LegacyAPIResponse:
+            raise RuntimeError("Unexpected state - cast_to is `APIResponse`")
+
+        if inspect.isclass(origin) and issubclass(origin, httpx.Response):
+            # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response
+            # and pass that class to our request functions. We cannot change the variance to be either
+            # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct
+            # the response class ourselves but that is something that should be supported directly in httpx
+            # as it would be easy to incorrectly construct the Response object due to the multitude of arguments.
+            if cast_to != httpx.Response:
+                raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`")
+            return cast(R, response)
+
+        # The check here is necessary as we are subverting the the type system
+        # with casts as the relationship between TypeVars and Types are very strict
+        # which means we must return *exactly* what was input or transform it in a
+        # way that retains the TypeVar state. As we cannot do that in this function
+        # then we have to resort to using `cast`. At the time of writing, we know this
+        # to be safe as we have handled all the types that could be bound to the
+        # `ResponseT` TypeVar, however if that TypeVar is ever updated in the future, then
+        # this function would become unsafe but a type checker would not report an error.
+        if (
+            cast_to is not object
+            and not origin is list
+            and not origin is dict
+            and not origin is Union
+            and not issubclass(origin, BaseModel)
+        ):
+            raise RuntimeError(
+                f"Invalid state, expected {cast_to} to be a subclass type of {BaseModel}, {dict}, {list} or {Union}."
+            )
+
+        # split is required to handle cases where additional information is included
+        # in the response, e.g. application/json; charset=utf-8
+        content_type, *_ = response.headers.get("content-type").split(";")
+        if content_type != "application/json":
+            if is_basemodel(cast_to):
+                try:
+                    data = response.json()
+                except Exception as exc:
+                    log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc)
+                else:
+                    return self._client._process_response_data(
+                        data=data,
+                        cast_to=cast_to,  # type: ignore
+                        response=response,
+                    )
+
+            if self._client._strict_response_validation:
+                raise APIResponseValidationError(
+                    response=response,
+                    message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.",
+                    body=response.text,
+                )
+
+            # If the API responds with content that isn't JSON then we just return
+            # the (decoded) text without performing any parsing so that you can still
+            # handle the response however you need to.
+            return response.text  # type: ignore
+
+        data = response.json()
+
+        return self._client._process_response_data(
+            data=data,
+            cast_to=cast_to,  # type: ignore
+            response=response,
+        )
+
+    @override
+    def __repr__(self) -> str:
+        return f"<APIResponse [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>"
+
+
+class MissingStreamClassError(TypeError):
+    def __init__(self) -> None:
+        super().__init__(
+            "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `openai._streaming` for reference",
+        )
+
+
+def _extract_stream_chunk_type(stream_cls: type) -> type:
+    args = get_args(stream_cls)
+    if not args:
+        raise TypeError(
+            f"Expected stream_cls to have been given a generic type argument, e.g. Stream[Foo] but received {stream_cls}",
+        )
+    return cast(type, args[0])
+
+
+def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, LegacyAPIResponse[R]]:
+    """Higher order function that takes one of our bound API methods and wraps it
+    to support returning the raw `APIResponse` object directly.
+    """
+
+    @functools.wraps(func)
+    def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]:
+        extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})}
+        extra_headers[RAW_RESPONSE_HEADER] = "true"
+
+        kwargs["extra_headers"] = extra_headers
+
+        return cast(LegacyAPIResponse[R], func(*args, **kwargs))
+
+    return wrapped
+
+
+def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[LegacyAPIResponse[R]]]:
+    """Higher order function that takes one of our bound API methods and wraps it
+    to support returning the raw `APIResponse` object directly.
+    """
+
+    @functools.wraps(func)
+    async def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]:
+        extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})}
+        extra_headers[RAW_RESPONSE_HEADER] = "true"
+
+        kwargs["extra_headers"] = extra_headers
+
+        return cast(LegacyAPIResponse[R], await func(*args, **kwargs))
+
+    return wrapped
+
+
+class HttpxBinaryResponseContent:
+    response: httpx.Response
+
+    def __init__(self, response: httpx.Response) -> None:
+        self.response = response
+
+    @property
+    def content(self) -> bytes:
+        return self.response.content
+
+    @property
+    def text(self) -> str:
+        return self.response.text
+
+    @property
+    def encoding(self) -> str | None:
+        return self.response.encoding
+
+    @property
+    def charset_encoding(self) -> str | None:
+        return self.response.charset_encoding
+
+    def json(self, **kwargs: Any) -> Any:
+        return self.response.json(**kwargs)
+
+    def read(self) -> bytes:
+        return self.response.read()
+
+    def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]:
+        return self.response.iter_bytes(chunk_size)
+
+    def iter_text(self, chunk_size: int | None = None) -> Iterator[str]:
+        return self.response.iter_text(chunk_size)
+
+    def iter_lines(self) -> Iterator[str]:
+        return self.response.iter_lines()
+
+    def iter_raw(self, chunk_size: int | None = None) -> Iterator[bytes]:
+        return self.response.iter_raw(chunk_size)
+
+    @deprecated(
+        "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead"
+    )
+    def stream_to_file(
+        self,
+        file: str | os.PathLike[str],
+        *,
+        chunk_size: int | None = None,
+    ) -> None:
+        with open(file, mode="wb") as f:
+            for data in self.response.iter_bytes(chunk_size):
+                f.write(data)
+
+    def close(self) -> None:
+        return self.response.close()
+
+    async def aread(self) -> bytes:
+        return await self.response.aread()
+
+    async def aiter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]:
+        return self.response.aiter_bytes(chunk_size)
+
+    async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]:
+        return self.response.aiter_text(chunk_size)
+
+    async def aiter_lines(self) -> AsyncIterator[str]:
+        return self.response.aiter_lines()
+
+    async def aiter_raw(self, chunk_size: int | None = None) -> AsyncIterator[bytes]:
+        return self.response.aiter_raw(chunk_size)
+
+    @deprecated(
+        "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead"
+    )
+    async def astream_to_file(
+        self,
+        file: str | os.PathLike[str],
+        *,
+        chunk_size: int | None = None,
+    ) -> None:
+        path = anyio.Path(file)
+        async with await path.open(mode="wb") as f:
+            async for data in self.response.aiter_bytes(chunk_size):
+                await f.write(data)
+
+    async def aclose(self) -> None:
+        return await self.response.aclose()
src/openai/_response.py
@@ -1,19 +1,32 @@
 from __future__ import annotations
 
+import os
 import inspect
 import logging
 import datetime
 import functools
-from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast
+from types import TracebackType
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Union,
+    Generic,
+    TypeVar,
+    Callable,
+    Iterator,
+    AsyncIterator,
+    cast,
+)
 from typing_extensions import Awaitable, ParamSpec, override, get_origin
 
+import anyio
 import httpx
 
-from ._types import NoneType, BinaryResponseContent
+from ._types import NoneType
 from ._utils import is_given, extract_type_var_from_base
 from ._models import BaseModel, is_basemodel
-from ._constants import RAW_RESPONSE_HEADER
-from ._exceptions import APIResponseValidationError
+from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER
+from ._exceptions import OpenAIError, APIResponseValidationError
 
 if TYPE_CHECKING:
     from ._models import FinalRequestOptions
@@ -22,15 +35,17 @@ if TYPE_CHECKING:
 
 P = ParamSpec("P")
 R = TypeVar("R")
+_APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]")
+_AsyncAPIResponseT = TypeVar("_AsyncAPIResponseT", bound="AsyncAPIResponse[Any]")
 
 log: logging.Logger = logging.getLogger(__name__)
 
 
-class APIResponse(Generic[R]):
+class BaseAPIResponse(Generic[R]):
     _cast_to: type[R]
     _client: BaseClient[Any, Any]
     _parsed: R | None
-    _stream: bool
+    _is_sse_stream: bool
     _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None
     _options: FinalRequestOptions
 
@@ -49,28 +64,18 @@ class APIResponse(Generic[R]):
         self._cast_to = cast_to
         self._client = client
         self._parsed = None
-        self._stream = stream
+        self._is_sse_stream = stream
         self._stream_cls = stream_cls
         self._options = options
         self.http_response = raw
 
-    def parse(self) -> R:
-        if self._parsed is not None:
-            return self._parsed
-
-        parsed = self._parse()
-        if is_given(self._options.post_parser):
-            parsed = self._options.post_parser(parsed)
-
-        self._parsed = parsed
-        return parsed
-
     @property
     def headers(self) -> httpx.Headers:
         return self.http_response.headers
 
     @property
     def http_request(self) -> httpx.Request:
+        """Returns the httpx Request instance associated with the current response."""
         return self.http_response.request
 
     @property
@@ -79,20 +84,13 @@ class APIResponse(Generic[R]):
 
     @property
     def url(self) -> httpx.URL:
+        """Returns the URL for which the request was made."""
         return self.http_response.url
 
     @property
     def method(self) -> str:
         return self.http_request.method
 
-    @property
-    def content(self) -> bytes:
-        return self.http_response.content
-
-    @property
-    def text(self) -> str:
-        return self.http_response.text
-
     @property
     def http_version(self) -> str:
         return self.http_response.http_version
@@ -102,13 +100,29 @@ class APIResponse(Generic[R]):
         """The time taken for the complete request/response cycle to complete."""
         return self.http_response.elapsed
 
+    @property
+    def is_closed(self) -> bool:
+        """Whether or not the response body has been closed.
+
+        If this is False then there is response data that has not been read yet.
+        You must either fully consume the response body or call `.close()`
+        before discarding the response to prevent resource leaks.
+        """
+        return self.http_response.is_closed
+
+    @override
+    def __repr__(self) -> str:
+        return (
+            f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>"
+        )
+
     def _parse(self) -> R:
-        if self._stream:
+        if self._is_sse_stream:
             if self._stream_cls:
                 return cast(
                     R,
                     self._stream_cls(
-                        cast_to=_extract_stream_chunk_type(self._stream_cls),
+                        cast_to=extract_stream_chunk_type(self._stream_cls),
                         response=self.http_response,
                         client=cast(Any, self._client),
                     ),
@@ -135,9 +149,13 @@ class APIResponse(Generic[R]):
         if cast_to == str:
             return cast(R, response.text)
 
+        if cast_to == bytes:
+            return cast(R, response.content)
+
         origin = get_origin(cast_to) or cast_to
 
-        if inspect.isclass(origin) and issubclass(origin, BinaryResponseContent):
+        # handle the legacy binary response case
+        if inspect.isclass(cast_to) and cast_to.__name__ == "HttpxBinaryResponseContent":
             return cast(R, cast_to(response))  # type: ignore
 
         if origin == APIResponse:
@@ -208,9 +226,227 @@ class APIResponse(Generic[R]):
             response=response,
         )
 
-    @override
-    def __repr__(self) -> str:
-        return f"<APIResponse [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>"
+
+class APIResponse(BaseAPIResponse[R]):
+    def parse(self) -> R:
+        """Returns the rich python representation of this response's data.
+
+        For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`.
+        """
+        if self._parsed is not None:
+            return self._parsed
+
+        if not self._is_sse_stream:
+            self.read()
+
+        parsed = self._parse()
+        if is_given(self._options.post_parser):
+            parsed = self._options.post_parser(parsed)
+
+        self._parsed = parsed
+        return parsed
+
+    def read(self) -> bytes:
+        """Read and return the binary response content."""
+        try:
+            return self.http_response.read()
+        except httpx.StreamConsumed as exc:
+            # The default error raised by httpx isn't very
+            # helpful in our case so we re-raise it with
+            # a different error message.
+            raise StreamAlreadyConsumed() from exc
+
+    def text(self) -> str:
+        """Read and decode the response content into a string."""
+        self.read()
+        return self.http_response.text
+
+    def json(self) -> object:
+        """Read and decode the JSON response content."""
+        self.read()
+        return self.http_response.json()
+
+    def close(self) -> None:
+        """Close the response and release the connection.
+
+        Automatically called if the response body is read to completion.
+        """
+        self.http_response.close()
+
+    def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]:
+        """
+        A byte-iterator over the decoded response content.
+
+        This automatically handles gzip, deflate and brotli encoded responses.
+        """
+        for chunk in self.http_response.iter_bytes(chunk_size):
+            yield chunk
+
+    def iter_text(self, chunk_size: int | None = None) -> Iterator[str]:
+        """A str-iterator over the decoded response content
+        that handles both gzip, deflate, etc but also detects the content's
+        string encoding.
+        """
+        for chunk in self.http_response.iter_text(chunk_size):
+            yield chunk
+
+    def iter_lines(self) -> Iterator[str]:
+        """Like `iter_text()` but will only yield chunks for each line"""
+        for chunk in self.http_response.iter_lines():
+            yield chunk
+
+
+class AsyncAPIResponse(BaseAPIResponse[R]):
+    async def parse(self) -> R:
+        """Returns the rich python representation of this response's data.
+
+        For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`.
+        """
+        if self._parsed is not None:
+            return self._parsed
+
+        if not self._is_sse_stream:
+            await self.read()
+
+        parsed = self._parse()
+        if is_given(self._options.post_parser):
+            parsed = self._options.post_parser(parsed)
+
+        self._parsed = parsed
+        return parsed
+
+    async def read(self) -> bytes:
+        """Read and return the binary response content."""
+        try:
+            return await self.http_response.aread()
+        except httpx.StreamConsumed as exc:
+            # the default error raised by httpx isn't very
+            # helpful in our case so we re-raise it with
+            # a different error message
+            raise StreamAlreadyConsumed() from exc
+
+    async def text(self) -> str:
+        """Read and decode the response content into a string."""
+        await self.read()
+        return self.http_response.text
+
+    async def json(self) -> object:
+        """Read and decode the JSON response content."""
+        await self.read()
+        return self.http_response.json()
+
+    async def close(self) -> None:
+        """Close the response and release the connection.
+
+        Automatically called if the response body is read to completion.
+        """
+        await self.http_response.aclose()
+
+    async def iter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]:
+        """
+        A byte-iterator over the decoded response content.
+
+        This automatically handles gzip, deflate and brotli encoded responses.
+        """
+        async for chunk in self.http_response.aiter_bytes(chunk_size):
+            yield chunk
+
+    async def iter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]:
+        """A str-iterator over the decoded response content
+        that handles both gzip, deflate, etc but also detects the content's
+        string encoding.
+        """
+        async for chunk in self.http_response.aiter_text(chunk_size):
+            yield chunk
+
+    async def iter_lines(self) -> AsyncIterator[str]:
+        """Like `iter_text()` but will only yield chunks for each line"""
+        async for chunk in self.http_response.aiter_lines():
+            yield chunk
+
+
+class BinaryAPIResponse(APIResponse[bytes]):
+    """Subclass of APIResponse providing helpers for dealing with binary data.
+
+    Note: If you want to stream the response data instead of eagerly reading it
+    all at once then you should use `.with_streaming_response` when making
+    the API request, e.g. `.with_streaming_response.get_binary_response()`
+    """
+
+    def write_to_file(
+        self,
+        file: str | os.PathLike[str],
+    ) -> None:
+        """Write the output to the given file.
+
+        Accepts a filename or any path-like object, e.g. pathlib.Path
+
+        Note: if you want to stream the data to the file instead of writing
+        all at once then you should use `.with_streaming_response` when making
+        the API request, e.g. `.with_streaming_response.get_binary_response()`
+        """
+        with open(file, mode="wb") as f:
+            for data in self.iter_bytes():
+                f.write(data)
+
+
+class AsyncBinaryAPIResponse(AsyncAPIResponse[bytes]):
+    """Subclass of APIResponse providing helpers for dealing with binary data.
+
+    Note: If you want to stream the response data instead of eagerly reading it
+    all at once then you should use `.with_streaming_response` when making
+    the API request, e.g. `.with_streaming_response.get_binary_response()`
+    """
+
+    async def write_to_file(
+        self,
+        file: str | os.PathLike[str],
+    ) -> None:
+        """Write the output to the given file.
+
+        Accepts a filename or any path-like object, e.g. pathlib.Path
+
+        Note: if you want to stream the data to the file instead of writing
+        all at once then you should use `.with_streaming_response` when making
+        the API request, e.g. `.with_streaming_response.get_binary_response()`
+        """
+        path = anyio.Path(file)
+        async with await path.open(mode="wb") as f:
+            async for data in self.iter_bytes():
+                await f.write(data)
+
+
+class StreamedBinaryAPIResponse(APIResponse[bytes]):
+    def stream_to_file(
+        self,
+        file: str | os.PathLike[str],
+        *,
+        chunk_size: int | None = None,
+    ) -> None:
+        """Streams the output to the given file.
+
+        Accepts a filename or any path-like object, e.g. pathlib.Path
+        """
+        with open(file, mode="wb") as f:
+            for data in self.iter_bytes(chunk_size):
+                f.write(data)
+
+
+class AsyncStreamedBinaryAPIResponse(AsyncAPIResponse[bytes]):
+    async def stream_to_file(
+        self,
+        file: str | os.PathLike[str],
+        *,
+        chunk_size: int | None = None,
+    ) -> None:
+        """Streams the output to the given file.
+
+        Accepts a filename or any path-like object, e.g. pathlib.Path
+        """
+        path = anyio.Path(file)
+        async with await path.open(mode="wb") as f:
+            async for data in self.iter_bytes(chunk_size):
+                await f.write(data)
 
 
 class MissingStreamClassError(TypeError):
@@ -220,14 +456,176 @@ class MissingStreamClassError(TypeError):
         )
 
 
-def _extract_stream_chunk_type(stream_cls: type) -> type:
-    from ._base_client import Stream, AsyncStream
+class StreamAlreadyConsumed(OpenAIError):
+    """
+    Attempted to read or stream content, but the content has already
+    been streamed.
 
-    return extract_type_var_from_base(
-        stream_cls,
-        index=0,
-        generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)),
-    )
+    This can happen if you use a method like `.iter_lines()` and then attempt
+    to read th entire response body afterwards, e.g.
+
+    ```py
+    response = await client.post(...)
+    async for line in response.iter_lines():
+        ...  # do something with `line`
+
+    content = await response.read()
+    # ^ error
+    ```
+
+    If you want this behaviour you'll need to either manually accumulate the response
+    content or call `await response.read()` before iterating over the stream.
+    """
+
+    def __init__(self) -> None:
+        message = (
+            "Attempted to read or stream some content, but the content has "
+            "already been streamed. "
+            "This could be due to attempting to stream the response "
+            "content more than once."
+            "\n\n"
+            "You can fix this by manually accumulating the response content while streaming "
+            "or by calling `.read()` before starting to stream."
+        )
+        super().__init__(message)
+
+
+class ResponseContextManager(Generic[_APIResponseT]):
+    """Context manager for ensuring that a request is not made
+    until it is entered and that the response will always be closed
+    when the context manager exits
+    """
+
+    def __init__(self, request_func: Callable[[], _APIResponseT]) -> None:
+        self._request_func = request_func
+        self.__response: _APIResponseT | None = None
+
+    def __enter__(self) -> _APIResponseT:
+        self.__response = self._request_func()
+        return self.__response
+
+    def __exit__(
+        self,
+        exc_type: type[BaseException] | None,
+        exc: BaseException | None,
+        exc_tb: TracebackType | None,
+    ) -> None:
+        if self.__response is not None:
+            self.__response.close()
+
+
+class AsyncResponseContextManager(Generic[_AsyncAPIResponseT]):
+    """Context manager for ensuring that a request is not made
+    until it is entered and that the response will always be closed
+    when the context manager exits
+    """
+
+    def __init__(self, api_request: Awaitable[_AsyncAPIResponseT]) -> None:
+        self._api_request = api_request
+        self.__response: _AsyncAPIResponseT | None = None
+
+    async def __aenter__(self) -> _AsyncAPIResponseT:
+        self.__response = await self._api_request
+        return self.__response
+
+    async def __aexit__(
+        self,
+        exc_type: type[BaseException] | None,
+        exc: BaseException | None,
+        exc_tb: TracebackType | None,
+    ) -> None:
+        if self.__response is not None:
+            await self.__response.close()
+
+
+def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseContextManager[APIResponse[R]]]:
+    """Higher order function that takes one of our bound API methods and wraps it
+    to support streaming and returning the raw `APIResponse` object directly.
+    """
+
+    @functools.wraps(func)
+    def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]:
+        extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})}
+        extra_headers[RAW_RESPONSE_HEADER] = "stream"
+
+        kwargs["extra_headers"] = extra_headers
+
+        make_request = functools.partial(func, *args, **kwargs)
+
+        return ResponseContextManager(cast(Callable[[], APIResponse[R]], make_request))
+
+    return wrapped
+
+
+def async_to_streamed_response_wrapper(
+    func: Callable[P, Awaitable[R]],
+) -> Callable[P, AsyncResponseContextManager[AsyncAPIResponse[R]]]:
+    """Higher order function that takes one of our bound API methods and wraps it
+    to support streaming and returning the raw `APIResponse` object directly.
+    """
+
+    @functools.wraps(func)
+    def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]:
+        extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})}
+        extra_headers[RAW_RESPONSE_HEADER] = "stream"
+
+        kwargs["extra_headers"] = extra_headers
+
+        make_request = func(*args, **kwargs)
+
+        return AsyncResponseContextManager(cast(Awaitable[AsyncAPIResponse[R]], make_request))
+
+    return wrapped
+
+
+def to_custom_streamed_response_wrapper(
+    func: Callable[P, object],
+    response_cls: type[_APIResponseT],
+) -> Callable[P, ResponseContextManager[_APIResponseT]]:
+    """Higher order function that takes one of our bound API methods and an `APIResponse` class
+    and wraps the method to support streaming and returning the given response class directly.
+
+    Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])`
+    """
+
+    @functools.wraps(func)
+    def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]:
+        extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})}
+        extra_headers[RAW_RESPONSE_HEADER] = "stream"
+        extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls
+
+        kwargs["extra_headers"] = extra_headers
+
+        make_request = functools.partial(func, *args, **kwargs)
+
+        return ResponseContextManager(cast(Callable[[], _APIResponseT], make_request))
+
+    return wrapped
+
+
+def async_to_custom_streamed_response_wrapper(
+    func: Callable[P, Awaitable[object]],
+    response_cls: type[_AsyncAPIResponseT],
+) -> Callable[P, AsyncResponseContextManager[_AsyncAPIResponseT]]:
+    """Higher order function that takes one of our bound API methods and an `APIResponse` class
+    and wraps the method to support streaming and returning the given response class directly.
+
+    Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])`
+    """
+
+    @functools.wraps(func)
+    def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]:
+        extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})}
+        extra_headers[RAW_RESPONSE_HEADER] = "stream"
+        extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls
+
+        kwargs["extra_headers"] = extra_headers
+
+        make_request = func(*args, **kwargs)
+
+        return AsyncResponseContextManager(cast(Awaitable[_AsyncAPIResponseT], make_request))
+
+    return wrapped
 
 
 def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]:
@@ -238,7 +636,7 @@ def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]
     @functools.wraps(func)
     def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]:
         extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})}
-        extra_headers[RAW_RESPONSE_HEADER] = "true"
+        extra_headers[RAW_RESPONSE_HEADER] = "raw"
 
         kwargs["extra_headers"] = extra_headers
 
@@ -247,18 +645,102 @@ def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]
     return wrapped
 
 
-def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[APIResponse[R]]]:
+def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]:
     """Higher order function that takes one of our bound API methods and wraps it
     to support returning the raw `APIResponse` object directly.
     """
 
     @functools.wraps(func)
-    async def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]:
+    async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]:
         extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})}
-        extra_headers[RAW_RESPONSE_HEADER] = "true"
+        extra_headers[RAW_RESPONSE_HEADER] = "raw"
 
         kwargs["extra_headers"] = extra_headers
 
-        return cast(APIResponse[R], await func(*args, **kwargs))
+        return cast(AsyncAPIResponse[R], await func(*args, **kwargs))
 
     return wrapped
+
+
+def to_custom_raw_response_wrapper(
+    func: Callable[P, object],
+    response_cls: type[_APIResponseT],
+) -> Callable[P, _APIResponseT]:
+    """Higher order function that takes one of our bound API methods and an `APIResponse` class
+    and wraps the method to support returning the given response class directly.
+
+    Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])`
+    """
+
+    @functools.wraps(func)
+    def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT:
+        extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})}
+        extra_headers[RAW_RESPONSE_HEADER] = "raw"
+        extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls
+
+        kwargs["extra_headers"] = extra_headers
+
+        return cast(_APIResponseT, func(*args, **kwargs))
+
+    return wrapped
+
+
+def async_to_custom_raw_response_wrapper(
+    func: Callable[P, Awaitable[object]],
+    response_cls: type[_AsyncAPIResponseT],
+) -> Callable[P, Awaitable[_AsyncAPIResponseT]]:
+    """Higher order function that takes one of our bound API methods and an `APIResponse` class
+    and wraps the method to support returning the given response class directly.
+
+    Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])`
+    """
+
+    @functools.wraps(func)
+    def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]:
+        extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})}
+        extra_headers[RAW_RESPONSE_HEADER] = "raw"
+        extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls
+
+        kwargs["extra_headers"] = extra_headers
+
+        return cast(Awaitable[_AsyncAPIResponseT], func(*args, **kwargs))
+
+    return wrapped
+
+
+def extract_stream_chunk_type(stream_cls: type) -> type:
+    """Given a type like `Stream[T]`, returns the generic type variable `T`.
+
+    This also handles the case where a concrete subclass is given, e.g.
+    ```py
+    class MyStream(Stream[bytes]):
+        ...
+
+    extract_stream_chunk_type(MyStream) -> bytes
+    ```
+    """
+    from ._base_client import Stream, AsyncStream
+
+    return extract_type_var_from_base(
+        stream_cls,
+        index=0,
+        generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)),
+    )
+
+
+def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type:
+    """Given a type like `APIResponse[T]`, returns the generic type variable `T`.
+
+    This also handles the case where a concrete subclass is given, e.g.
+    ```py
+    class MyResponse(APIResponse[bytes]):
+        ...
+
+    extract_response_type(MyResponse) -> bytes
+    ```
+    """
+    return extract_type_var_from_base(
+        typ,
+        generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse)),
+        index=0,
+    )
src/openai/_types.py
@@ -1,7 +1,6 @@
 from __future__ import annotations
 
 from os import PathLike
-from abc import ABC, abstractmethod
 from typing import (
     IO,
     TYPE_CHECKING,
@@ -14,10 +13,8 @@ from typing import (
     Mapping,
     TypeVar,
     Callable,
-    Iterator,
     Optional,
     Sequence,
-    AsyncIterator,
 )
 from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable
 
@@ -27,6 +24,8 @@ from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTranspo
 
 if TYPE_CHECKING:
     from ._models import BaseModel
+    from ._response import APIResponse, AsyncAPIResponse
+    from ._legacy_response import HttpxBinaryResponseContent
 
 Transport = BaseTransport
 AsyncTransport = AsyncBaseTransport
@@ -37,162 +36,6 @@ ModelT = TypeVar("ModelT", bound=pydantic.BaseModel)
 _T = TypeVar("_T")
 
 
-class BinaryResponseContent(ABC):
-    @abstractmethod
-    def __init__(
-        self,
-        response: Any,
-    ) -> None:
-        ...
-
-    @property
-    @abstractmethod
-    def content(self) -> bytes:
-        pass
-
-    @property
-    @abstractmethod
-    def text(self) -> str:
-        pass
-
-    @property
-    @abstractmethod
-    def encoding(self) -> Optional[str]:
-        """
-        Return an encoding to use for decoding the byte content into text.
-        The priority for determining this is given by...
-
-        * `.encoding = <>` has been set explicitly.
-        * The encoding as specified by the charset parameter in the Content-Type header.
-        * The encoding as determined by `default_encoding`, which may either be
-          a string like "utf-8" indicating the encoding to use, or may be a callable
-          which enables charset autodetection.
-        """
-        pass
-
-    @property
-    @abstractmethod
-    def charset_encoding(self) -> Optional[str]:
-        """
-        Return the encoding, as specified by the Content-Type header.
-        """
-        pass
-
-    @abstractmethod
-    def json(self, **kwargs: Any) -> Any:
-        pass
-
-    @abstractmethod
-    def read(self) -> bytes:
-        """
-        Read and return the response content.
-        """
-        pass
-
-    @abstractmethod
-    def iter_bytes(self, chunk_size: Optional[int] = None) -> Iterator[bytes]:
-        """
-        A byte-iterator over the decoded response content.
-        This allows us to handle gzip, deflate, and brotli encoded responses.
-        """
-        pass
-
-    @abstractmethod
-    def iter_text(self, chunk_size: Optional[int] = None) -> Iterator[str]:
-        """
-        A str-iterator over the decoded response content
-        that handles both gzip, deflate, etc but also detects the content's
-        string encoding.
-        """
-        pass
-
-    @abstractmethod
-    def iter_lines(self) -> Iterator[str]:
-        pass
-
-    @abstractmethod
-    def iter_raw(self, chunk_size: Optional[int] = None) -> Iterator[bytes]:
-        """
-        A byte-iterator over the raw response content.
-        """
-        pass
-
-    @abstractmethod
-    def stream_to_file(
-        self,
-        file: str | PathLike[str],
-        *,
-        chunk_size: int | None = None,
-    ) -> None:
-        """
-        Stream the output to the given file.
-        """
-        pass
-
-    @abstractmethod
-    def close(self) -> None:
-        """
-        Close the response and release the connection.
-        Automatically called if the response body is read to completion.
-        """
-        pass
-
-    @abstractmethod
-    async def aread(self) -> bytes:
-        """
-        Read and return the response content.
-        """
-        pass
-
-    @abstractmethod
-    async def aiter_bytes(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]:
-        """
-        A byte-iterator over the decoded response content.
-        This allows us to handle gzip, deflate, and brotli encoded responses.
-        """
-        pass
-
-    @abstractmethod
-    async def aiter_text(self, chunk_size: Optional[int] = None) -> AsyncIterator[str]:
-        """
-        A str-iterator over the decoded response content
-        that handles both gzip, deflate, etc but also detects the content's
-        string encoding.
-        """
-        pass
-
-    @abstractmethod
-    async def aiter_lines(self) -> AsyncIterator[str]:
-        pass
-
-    @abstractmethod
-    async def aiter_raw(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]:
-        """
-        A byte-iterator over the raw response content.
-        """
-        pass
-
-    @abstractmethod
-    async def astream_to_file(
-        self,
-        file: str | PathLike[str],
-        *,
-        chunk_size: int | None = None,
-    ) -> None:
-        """
-        Stream the output to the given file.
-        """
-        pass
-
-    @abstractmethod
-    async def aclose(self) -> None:
-        """
-        Close the response and release the connection.
-        Automatically called if the response body is read to completion.
-        """
-        pass
-
-
 # Approximates httpx internal ProxiesTypes and RequestFiles types
 # while adding support for `PathLike` instances
 ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]]
@@ -343,7 +186,9 @@ ResponseT = TypeVar(
         Dict[str, Any],
         Response,
         ModelBuilderProtocol,
-        BinaryResponseContent,
+        "APIResponse[Any]",
+        "AsyncAPIResponse[Any]",
+        "HttpxBinaryResponseContent",
     ],
 )
 
@@ -359,6 +204,7 @@ PostParser = Callable[[Any], Any]
 @runtime_checkable
 class InheritsGeneric(Protocol):
     """Represents a type that has inherited from `Generic`
+
     The `__orig_bases__` property can be used to determine the resolved
     type variable for a given base class.
     """
tests/api_resources/audio/test_speech.py
@@ -3,15 +3,19 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import httpx
 import pytest
 from respx import MockRouter
 
+import openai._legacy_response as _legacy_response
 from openai import OpenAI, AsyncOpenAI
-from openai._types import BinaryResponseContent
+from tests.utils import assert_matches_type
 from openai._client import OpenAI, AsyncOpenAI
 
+# pyright: reportDeprecated=false
+
 base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
 api_key = "My API Key"
 
@@ -21,7 +25,6 @@ class TestSpeech:
     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"])
 
-    @pytest.mark.skip(reason="Mocked tests are currently broken")
     @parametrize
     @pytest.mark.respx(base_url=base_url)
     def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
@@ -31,10 +34,9 @@ class TestSpeech:
             model="string",
             voice="alloy",
         )
-        assert isinstance(speech, BinaryResponseContent)
+        assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent)
         assert speech.json() == {"foo": "bar"}
 
-    @pytest.mark.skip(reason="Mocked tests are currently broken")
     @parametrize
     @pytest.mark.respx(base_url=base_url)
     def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None:
@@ -46,23 +48,41 @@ class TestSpeech:
             response_format="mp3",
             speed=0.25,
         )
-        assert isinstance(speech, BinaryResponseContent)
+        assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent)
         assert speech.json() == {"foo": "bar"}
 
-    @pytest.mark.skip(reason="Mocked tests are currently broken")
     @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.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         speech = response.parse()
-        assert isinstance(speech, BinaryResponseContent)
-        assert speech.json() == {"foo": "bar"}
+        assert_matches_type(_legacy_response.HttpxBinaryResponseContent, speech, path=["response"])
+
+    @parametrize
+    @pytest.mark.respx(base_url=base_url)
+    def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
+        respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        with client.audio.speech.with_streaming_response.create(
+            input="string",
+            model="string",
+            voice="alloy",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            speech = response.parse()
+            assert_matches_type(bytes, speech, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
 
 
 class TestAsyncSpeech:
@@ -70,7 +90,6 @@ class TestAsyncSpeech:
     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"])
 
-    @pytest.mark.skip(reason="Mocked tests are currently broken")
     @parametrize
     @pytest.mark.respx(base_url=base_url)
     async def test_method_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None:
@@ -80,10 +99,9 @@ class TestAsyncSpeech:
             model="string",
             voice="alloy",
         )
-        assert isinstance(speech, BinaryResponseContent)
+        assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent)
         assert speech.json() == {"foo": "bar"}
 
-    @pytest.mark.skip(reason="Mocked tests are currently broken")
     @parametrize
     @pytest.mark.respx(base_url=base_url)
     async def test_method_create_with_all_params(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None:
@@ -95,20 +113,38 @@ class TestAsyncSpeech:
             response_format="mp3",
             speed=0.25,
         )
-        assert isinstance(speech, BinaryResponseContent)
+        assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent)
         assert speech.json() == {"foo": "bar"}
 
-    @pytest.mark.skip(reason="Mocked tests are currently broken")
     @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.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         speech = response.parse()
-        assert isinstance(speech, BinaryResponseContent)
-        assert speech.json() == {"foo": "bar"}
+        assert_matches_type(_legacy_response.HttpxBinaryResponseContent, speech, path=["response"])
+
+    @parametrize
+    @pytest.mark.respx(base_url=base_url)
+    async def test_streaming_response_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None:
+        respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        async with client.audio.speech.with_streaming_response.create(
+            input="string",
+            model="string",
+            voice="alloy",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            speech = await response.parse()
+            assert_matches_type(bytes, speech, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/audio/test_transcriptions.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -46,10 +47,26 @@ class TestTranscriptions:
             file=b"raw file contents",
             model="whisper-1",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         transcription = response.parse()
         assert_matches_type(Transcription, transcription, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.audio.transcriptions.with_streaming_response.create(
+            file=b"raw file contents",
+            model="whisper-1",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            transcription = response.parse()
+            assert_matches_type(Transcription, transcription, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncTranscriptions:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -82,6 +99,22 @@ class TestAsyncTranscriptions:
             file=b"raw file contents",
             model="whisper-1",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         transcription = response.parse()
         assert_matches_type(Transcription, transcription, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.audio.transcriptions.with_streaming_response.create(
+            file=b"raw file contents",
+            model="whisper-1",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            transcription = await response.parse()
+            assert_matches_type(Transcription, transcription, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/audio/test_translations.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -45,10 +46,26 @@ class TestTranslations:
             file=b"raw file contents",
             model="whisper-1",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         translation = response.parse()
         assert_matches_type(Translation, translation, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.audio.translations.with_streaming_response.create(
+            file=b"raw file contents",
+            model="whisper-1",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            translation = response.parse()
+            assert_matches_type(Translation, translation, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncTranslations:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -80,6 +97,22 @@ class TestAsyncTranslations:
             file=b"raw file contents",
             model="whisper-1",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         translation = response.parse()
         assert_matches_type(Translation, translation, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.audio.translations.with_streaming_response.create(
+            file=b"raw file contents",
+            model="whisper-1",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            translation = await response.parse()
+            assert_matches_type(Translation, translation, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/beta/assistants/test_files.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -35,10 +36,26 @@ class TestFiles:
             "file-abc123",
             file_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(AssistantFile, file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.beta.assistants.files.with_streaming_response.create(
+            "file-abc123",
+            file_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(AssistantFile, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_retrieve(self, client: OpenAI) -> None:
         file = client.beta.assistants.files.retrieve(
@@ -53,10 +70,26 @@ class TestFiles:
             "string",
             assistant_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(AssistantFile, file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+        with client.beta.assistants.files.with_streaming_response.retrieve(
+            "string",
+            assistant_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(AssistantFile, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_list(self, client: OpenAI) -> None:
         file = client.beta.assistants.files.list(
@@ -80,10 +113,25 @@ class TestFiles:
         response = client.beta.assistants.files.with_raw_response.list(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_list(self, client: OpenAI) -> None:
+        with client.beta.assistants.files.with_streaming_response.list(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_delete(self, client: OpenAI) -> None:
         file = client.beta.assistants.files.delete(
@@ -98,10 +146,26 @@ class TestFiles:
             "string",
             assistant_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(FileDeleteResponse, file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_delete(self, client: OpenAI) -> None:
+        with client.beta.assistants.files.with_streaming_response.delete(
+            "string",
+            assistant_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(FileDeleteResponse, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncFiles:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -122,10 +186,26 @@ class TestAsyncFiles:
             "file-abc123",
             file_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(AssistantFile, file, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.beta.assistants.files.with_streaming_response.create(
+            "file-abc123",
+            file_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(AssistantFile, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
         file = await client.beta.assistants.files.retrieve(
@@ -140,10 +220,26 @@ class TestAsyncFiles:
             "string",
             assistant_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(AssistantFile, file, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None:
+        async with client.beta.assistants.files.with_streaming_response.retrieve(
+            "string",
+            assistant_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(AssistantFile, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_list(self, client: AsyncOpenAI) -> None:
         file = await client.beta.assistants.files.list(
@@ -167,10 +263,25 @@ class TestAsyncFiles:
         response = await client.beta.assistants.files.with_raw_response.list(
             "string",
         )
+
+        assert response.is_closed is True
         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_streaming_response_list(self, client: AsyncOpenAI) -> None:
+        async with client.beta.assistants.files.with_streaming_response.list(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_delete(self, client: AsyncOpenAI) -> None:
         file = await client.beta.assistants.files.delete(
@@ -185,6 +296,22 @@ class TestAsyncFiles:
             "string",
             assistant_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(FileDeleteResponse, file, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None:
+        async with client.beta.assistants.files.with_streaming_response.delete(
+            "string",
+            assistant_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(FileDeleteResponse, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/beta/threads/messages/test_files.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -37,10 +38,27 @@ class TestFiles:
             thread_id="thread_abc123",
             message_id="msg_abc123",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(MessageFile, file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+        with client.beta.threads.messages.files.with_streaming_response.retrieve(
+            "file-abc123",
+            thread_id="thread_abc123",
+            message_id="msg_abc123",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(MessageFile, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_list(self, client: OpenAI) -> None:
         file = client.beta.threads.messages.files.list(
@@ -67,10 +85,26 @@ class TestFiles:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_list(self, client: OpenAI) -> None:
+        with client.beta.threads.messages.files.with_streaming_response.list(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncFiles:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -93,10 +127,27 @@ class TestAsyncFiles:
             thread_id="thread_abc123",
             message_id="msg_abc123",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(MessageFile, file, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.messages.files.with_streaming_response.retrieve(
+            "file-abc123",
+            thread_id="thread_abc123",
+            message_id="msg_abc123",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(MessageFile, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_list(self, client: AsyncOpenAI) -> None:
         file = await client.beta.threads.messages.files.list(
@@ -123,6 +174,22 @@ class TestAsyncFiles:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_list(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.messages.files.with_streaming_response.list(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/beta/threads/runs/test_steps.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -37,10 +38,27 @@ class TestSteps:
             thread_id="string",
             run_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         step = response.parse()
         assert_matches_type(RunStep, step, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+        with client.beta.threads.runs.steps.with_streaming_response.retrieve(
+            "string",
+            thread_id="string",
+            run_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            step = response.parse()
+            assert_matches_type(RunStep, step, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_list(self, client: OpenAI) -> None:
         step = client.beta.threads.runs.steps.list(
@@ -67,10 +85,26 @@ class TestSteps:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         step = response.parse()
         assert_matches_type(SyncCursorPage[RunStep], step, path=["response"])
 
+    @parametrize
+    def test_streaming_response_list(self, client: OpenAI) -> None:
+        with client.beta.threads.runs.steps.with_streaming_response.list(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            step = response.parse()
+            assert_matches_type(SyncCursorPage[RunStep], step, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncSteps:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -93,10 +127,27 @@ class TestAsyncSteps:
             thread_id="string",
             run_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         step = response.parse()
         assert_matches_type(RunStep, step, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.runs.steps.with_streaming_response.retrieve(
+            "string",
+            thread_id="string",
+            run_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            step = await response.parse()
+            assert_matches_type(RunStep, step, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_list(self, client: AsyncOpenAI) -> None:
         step = await client.beta.threads.runs.steps.list(
@@ -123,6 +174,22 @@ class TestAsyncSteps:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         step = response.parse()
         assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_list(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.runs.steps.with_streaming_response.list(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            step = await response.parse()
+            assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/beta/threads/test_messages.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -48,10 +49,27 @@ class TestMessages:
             content="x",
             role="user",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         message = response.parse()
         assert_matches_type(ThreadMessage, message, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.beta.threads.messages.with_streaming_response.create(
+            "string",
+            content="x",
+            role="user",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            message = response.parse()
+            assert_matches_type(ThreadMessage, message, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_retrieve(self, client: OpenAI) -> None:
         message = client.beta.threads.messages.retrieve(
@@ -66,10 +84,26 @@ class TestMessages:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         message = response.parse()
         assert_matches_type(ThreadMessage, message, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+        with client.beta.threads.messages.with_streaming_response.retrieve(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            message = response.parse()
+            assert_matches_type(ThreadMessage, message, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_update(self, client: OpenAI) -> None:
         message = client.beta.threads.messages.update(
@@ -93,10 +127,26 @@ class TestMessages:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         message = response.parse()
         assert_matches_type(ThreadMessage, message, path=["response"])
 
+    @parametrize
+    def test_streaming_response_update(self, client: OpenAI) -> None:
+        with client.beta.threads.messages.with_streaming_response.update(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            message = response.parse()
+            assert_matches_type(ThreadMessage, message, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_list(self, client: OpenAI) -> None:
         message = client.beta.threads.messages.list(
@@ -120,10 +170,25 @@ class TestMessages:
         response = client.beta.threads.messages.with_raw_response.list(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         message = response.parse()
         assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"])
 
+    @parametrize
+    def test_streaming_response_list(self, client: OpenAI) -> None:
+        with client.beta.threads.messages.with_streaming_response.list(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            message = response.parse()
+            assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncMessages:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -157,10 +222,27 @@ class TestAsyncMessages:
             content="x",
             role="user",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         message = response.parse()
         assert_matches_type(ThreadMessage, message, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.messages.with_streaming_response.create(
+            "string",
+            content="x",
+            role="user",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            message = await response.parse()
+            assert_matches_type(ThreadMessage, message, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
         message = await client.beta.threads.messages.retrieve(
@@ -175,10 +257,26 @@ class TestAsyncMessages:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         message = response.parse()
         assert_matches_type(ThreadMessage, message, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.messages.with_streaming_response.retrieve(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            message = await response.parse()
+            assert_matches_type(ThreadMessage, message, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_update(self, client: AsyncOpenAI) -> None:
         message = await client.beta.threads.messages.update(
@@ -202,10 +300,26 @@ class TestAsyncMessages:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         message = response.parse()
         assert_matches_type(ThreadMessage, message, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_update(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.messages.with_streaming_response.update(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            message = await response.parse()
+            assert_matches_type(ThreadMessage, message, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_list(self, client: AsyncOpenAI) -> None:
         message = await client.beta.threads.messages.list(
@@ -229,6 +343,21 @@ class TestAsyncMessages:
         response = await client.beta.threads.messages.with_raw_response.list(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         message = response.parse()
         assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_list(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.messages.with_streaming_response.list(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            message = await response.parse()
+            assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/beta/threads/test_runs.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -50,10 +51,26 @@ class TestRuns:
             "string",
             assistant_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(Run, run, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.beta.threads.runs.with_streaming_response.create(
+            "string",
+            assistant_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = response.parse()
+            assert_matches_type(Run, run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_retrieve(self, client: OpenAI) -> None:
         run = client.beta.threads.runs.retrieve(
@@ -68,10 +85,26 @@ class TestRuns:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(Run, run, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+        with client.beta.threads.runs.with_streaming_response.retrieve(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = response.parse()
+            assert_matches_type(Run, run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_update(self, client: OpenAI) -> None:
         run = client.beta.threads.runs.update(
@@ -95,10 +128,26 @@ class TestRuns:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(Run, run, path=["response"])
 
+    @parametrize
+    def test_streaming_response_update(self, client: OpenAI) -> None:
+        with client.beta.threads.runs.with_streaming_response.update(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = response.parse()
+            assert_matches_type(Run, run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_list(self, client: OpenAI) -> None:
         run = client.beta.threads.runs.list(
@@ -122,10 +171,25 @@ class TestRuns:
         response = client.beta.threads.runs.with_raw_response.list(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(SyncCursorPage[Run], run, path=["response"])
 
+    @parametrize
+    def test_streaming_response_list(self, client: OpenAI) -> None:
+        with client.beta.threads.runs.with_streaming_response.list(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = response.parse()
+            assert_matches_type(SyncCursorPage[Run], run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_cancel(self, client: OpenAI) -> None:
         run = client.beta.threads.runs.cancel(
@@ -140,10 +204,26 @@ class TestRuns:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(Run, run, path=["response"])
 
+    @parametrize
+    def test_streaming_response_cancel(self, client: OpenAI) -> None:
+        with client.beta.threads.runs.with_streaming_response.cancel(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = response.parse()
+            assert_matches_type(Run, run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_submit_tool_outputs(self, client: OpenAI) -> None:
         run = client.beta.threads.runs.submit_tool_outputs(
@@ -160,10 +240,27 @@ class TestRuns:
             thread_id="string",
             tool_outputs=[{}, {}, {}],
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(Run, run, path=["response"])
 
+    @parametrize
+    def test_streaming_response_submit_tool_outputs(self, client: OpenAI) -> None:
+        with client.beta.threads.runs.with_streaming_response.submit_tool_outputs(
+            "string",
+            thread_id="string",
+            tool_outputs=[{}, {}, {}],
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = response.parse()
+            assert_matches_type(Run, run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncRuns:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -197,10 +294,26 @@ class TestAsyncRuns:
             "string",
             assistant_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(Run, run, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.runs.with_streaming_response.create(
+            "string",
+            assistant_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = await response.parse()
+            assert_matches_type(Run, run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
         run = await client.beta.threads.runs.retrieve(
@@ -215,10 +328,26 @@ class TestAsyncRuns:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(Run, run, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.runs.with_streaming_response.retrieve(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = await response.parse()
+            assert_matches_type(Run, run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_update(self, client: AsyncOpenAI) -> None:
         run = await client.beta.threads.runs.update(
@@ -242,10 +371,26 @@ class TestAsyncRuns:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(Run, run, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_update(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.runs.with_streaming_response.update(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = await response.parse()
+            assert_matches_type(Run, run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_list(self, client: AsyncOpenAI) -> None:
         run = await client.beta.threads.runs.list(
@@ -269,10 +414,25 @@ class TestAsyncRuns:
         response = await client.beta.threads.runs.with_raw_response.list(
             "string",
         )
+
+        assert response.is_closed is True
         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_streaming_response_list(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.runs.with_streaming_response.list(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = await response.parse()
+            assert_matches_type(AsyncCursorPage[Run], run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_cancel(self, client: AsyncOpenAI) -> None:
         run = await client.beta.threads.runs.cancel(
@@ -287,10 +447,26 @@ class TestAsyncRuns:
             "string",
             thread_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(Run, run, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_cancel(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.runs.with_streaming_response.cancel(
+            "string",
+            thread_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = await response.parse()
+            assert_matches_type(Run, run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_submit_tool_outputs(self, client: AsyncOpenAI) -> None:
         run = await client.beta.threads.runs.submit_tool_outputs(
@@ -307,6 +483,23 @@ class TestAsyncRuns:
             thread_id="string",
             tool_outputs=[{}, {}, {}],
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         run = response.parse()
         assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_submit_tool_outputs(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.runs.with_streaming_response.submit_tool_outputs(
+            "string",
+            thread_id="string",
+            tool_outputs=[{}, {}, {}],
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            run = await response.parse()
+            assert_matches_type(Run, run, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/beta/test_assistants.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -49,10 +50,25 @@ class TestAssistants:
         response = client.beta.assistants.with_raw_response.create(
             model="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         assistant = response.parse()
         assert_matches_type(Assistant, assistant, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.beta.assistants.with_streaming_response.create(
+            model="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            assistant = response.parse()
+            assert_matches_type(Assistant, assistant, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_retrieve(self, client: OpenAI) -> None:
         assistant = client.beta.assistants.retrieve(
@@ -65,10 +81,25 @@ class TestAssistants:
         response = client.beta.assistants.with_raw_response.retrieve(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         assistant = response.parse()
         assert_matches_type(Assistant, assistant, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+        with client.beta.assistants.with_streaming_response.retrieve(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            assistant = response.parse()
+            assert_matches_type(Assistant, assistant, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_update(self, client: OpenAI) -> None:
         assistant = client.beta.assistants.update(
@@ -95,10 +126,25 @@ class TestAssistants:
         response = client.beta.assistants.with_raw_response.update(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         assistant = response.parse()
         assert_matches_type(Assistant, assistant, path=["response"])
 
+    @parametrize
+    def test_streaming_response_update(self, client: OpenAI) -> None:
+        with client.beta.assistants.with_streaming_response.update(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            assistant = response.parse()
+            assert_matches_type(Assistant, assistant, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_list(self, client: OpenAI) -> None:
         assistant = client.beta.assistants.list()
@@ -117,10 +163,23 @@ class TestAssistants:
     @parametrize
     def test_raw_response_list(self, client: OpenAI) -> None:
         response = client.beta.assistants.with_raw_response.list()
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         assistant = response.parse()
         assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"])
 
+    @parametrize
+    def test_streaming_response_list(self, client: OpenAI) -> None:
+        with client.beta.assistants.with_streaming_response.list() as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            assistant = response.parse()
+            assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_delete(self, client: OpenAI) -> None:
         assistant = client.beta.assistants.delete(
@@ -133,10 +192,25 @@ class TestAssistants:
         response = client.beta.assistants.with_raw_response.delete(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         assistant = response.parse()
         assert_matches_type(AssistantDeleted, assistant, path=["response"])
 
+    @parametrize
+    def test_streaming_response_delete(self, client: OpenAI) -> None:
+        with client.beta.assistants.with_streaming_response.delete(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            assistant = response.parse()
+            assert_matches_type(AssistantDeleted, assistant, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncAssistants:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -168,10 +242,25 @@ class TestAsyncAssistants:
         response = await client.beta.assistants.with_raw_response.create(
             model="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         assistant = response.parse()
         assert_matches_type(Assistant, assistant, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.beta.assistants.with_streaming_response.create(
+            model="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            assistant = await response.parse()
+            assert_matches_type(Assistant, assistant, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
         assistant = await client.beta.assistants.retrieve(
@@ -184,10 +273,25 @@ class TestAsyncAssistants:
         response = await client.beta.assistants.with_raw_response.retrieve(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         assistant = response.parse()
         assert_matches_type(Assistant, assistant, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None:
+        async with client.beta.assistants.with_streaming_response.retrieve(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            assistant = await response.parse()
+            assert_matches_type(Assistant, assistant, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_update(self, client: AsyncOpenAI) -> None:
         assistant = await client.beta.assistants.update(
@@ -214,10 +318,25 @@ class TestAsyncAssistants:
         response = await client.beta.assistants.with_raw_response.update(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         assistant = response.parse()
         assert_matches_type(Assistant, assistant, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_update(self, client: AsyncOpenAI) -> None:
+        async with client.beta.assistants.with_streaming_response.update(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            assistant = await response.parse()
+            assert_matches_type(Assistant, assistant, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_list(self, client: AsyncOpenAI) -> None:
         assistant = await client.beta.assistants.list()
@@ -236,10 +355,23 @@ class TestAsyncAssistants:
     @parametrize
     async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
         response = await client.beta.assistants.with_raw_response.list()
+
+        assert response.is_closed is True
         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_streaming_response_list(self, client: AsyncOpenAI) -> None:
+        async with client.beta.assistants.with_streaming_response.list() as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            assistant = await response.parse()
+            assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_delete(self, client: AsyncOpenAI) -> None:
         assistant = await client.beta.assistants.delete(
@@ -252,6 +384,21 @@ class TestAsyncAssistants:
         response = await client.beta.assistants.with_raw_response.delete(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         assistant = response.parse()
         assert_matches_type(AssistantDeleted, assistant, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None:
+        async with client.beta.assistants.with_streaming_response.delete(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            assistant = await response.parse()
+            assert_matches_type(AssistantDeleted, assistant, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/beta/test_threads.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -59,10 +60,23 @@ class TestThreads:
     @parametrize
     def test_raw_response_create(self, client: OpenAI) -> None:
         response = client.beta.threads.with_raw_response.create()
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         thread = response.parse()
         assert_matches_type(Thread, thread, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.beta.threads.with_streaming_response.create() as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            thread = response.parse()
+            assert_matches_type(Thread, thread, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_retrieve(self, client: OpenAI) -> None:
         thread = client.beta.threads.retrieve(
@@ -75,10 +89,25 @@ class TestThreads:
         response = client.beta.threads.with_raw_response.retrieve(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         thread = response.parse()
         assert_matches_type(Thread, thread, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+        with client.beta.threads.with_streaming_response.retrieve(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            thread = response.parse()
+            assert_matches_type(Thread, thread, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_update(self, client: OpenAI) -> None:
         thread = client.beta.threads.update(
@@ -99,10 +128,25 @@ class TestThreads:
         response = client.beta.threads.with_raw_response.update(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         thread = response.parse()
         assert_matches_type(Thread, thread, path=["response"])
 
+    @parametrize
+    def test_streaming_response_update(self, client: OpenAI) -> None:
+        with client.beta.threads.with_streaming_response.update(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            thread = response.parse()
+            assert_matches_type(Thread, thread, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_delete(self, client: OpenAI) -> None:
         thread = client.beta.threads.delete(
@@ -115,10 +159,25 @@ class TestThreads:
         response = client.beta.threads.with_raw_response.delete(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         thread = response.parse()
         assert_matches_type(ThreadDeleted, thread, path=["response"])
 
+    @parametrize
+    def test_streaming_response_delete(self, client: OpenAI) -> None:
+        with client.beta.threads.with_streaming_response.delete(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            thread = response.parse()
+            assert_matches_type(ThreadDeleted, thread, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_create_and_run(self, client: OpenAI) -> None:
         thread = client.beta.threads.create_and_run(
@@ -165,10 +224,25 @@ class TestThreads:
         response = client.beta.threads.with_raw_response.create_and_run(
             assistant_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         thread = response.parse()
         assert_matches_type(Run, thread, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create_and_run(self, client: OpenAI) -> None:
+        with client.beta.threads.with_streaming_response.create_and_run(
+            assistant_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            thread = response.parse()
+            assert_matches_type(Run, thread, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncThreads:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -210,10 +284,23 @@ class TestAsyncThreads:
     @parametrize
     async def test_raw_response_create(self, client: AsyncOpenAI) -> None:
         response = await client.beta.threads.with_raw_response.create()
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         thread = response.parse()
         assert_matches_type(Thread, thread, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.with_streaming_response.create() as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            thread = await response.parse()
+            assert_matches_type(Thread, thread, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
         thread = await client.beta.threads.retrieve(
@@ -226,10 +313,25 @@ class TestAsyncThreads:
         response = await client.beta.threads.with_raw_response.retrieve(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         thread = response.parse()
         assert_matches_type(Thread, thread, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.with_streaming_response.retrieve(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            thread = await response.parse()
+            assert_matches_type(Thread, thread, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_update(self, client: AsyncOpenAI) -> None:
         thread = await client.beta.threads.update(
@@ -250,10 +352,25 @@ class TestAsyncThreads:
         response = await client.beta.threads.with_raw_response.update(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         thread = response.parse()
         assert_matches_type(Thread, thread, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_update(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.with_streaming_response.update(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            thread = await response.parse()
+            assert_matches_type(Thread, thread, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_delete(self, client: AsyncOpenAI) -> None:
         thread = await client.beta.threads.delete(
@@ -266,10 +383,25 @@ class TestAsyncThreads:
         response = await client.beta.threads.with_raw_response.delete(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         thread = response.parse()
         assert_matches_type(ThreadDeleted, thread, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.with_streaming_response.delete(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            thread = await response.parse()
+            assert_matches_type(ThreadDeleted, thread, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_create_and_run(self, client: AsyncOpenAI) -> None:
         thread = await client.beta.threads.create_and_run(
@@ -316,6 +448,21 @@ class TestAsyncThreads:
         response = await client.beta.threads.with_raw_response.create_and_run(
             assistant_id="string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         thread = response.parse()
         assert_matches_type(Run, thread, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_create_and_run(self, client: AsyncOpenAI) -> None:
+        async with client.beta.threads.with_streaming_response.create_and_run(
+            assistant_id="string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            thread = await response.parse()
+            assert_matches_type(Run, thread, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/chat/test_completions.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -107,13 +108,34 @@ class TestCompletions:
             ],
             model="gpt-3.5-turbo",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         completion = response.parse()
         assert_matches_type(ChatCompletion, completion, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create_overload_1(self, client: OpenAI) -> None:
+        with client.chat.completions.with_streaming_response.create(
+            messages=[
+                {
+                    "content": "string",
+                    "role": "system",
+                }
+            ],
+            model="gpt-3.5-turbo",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            completion = response.parse()
+            assert_matches_type(ChatCompletion, completion, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_create_overload_2(self, client: OpenAI) -> None:
-        client.chat.completions.create(
+        completion_stream = client.chat.completions.create(
             messages=[
                 {
                     "content": "string",
@@ -123,10 +145,11 @@ class TestCompletions:
             model="gpt-3.5-turbo",
             stream=True,
         )
+        completion_stream.response.close()
 
     @parametrize
     def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
-        client.chat.completions.create(
+        completion_stream = client.chat.completions.create(
             messages=[
                 {
                     "content": "string",
@@ -185,6 +208,7 @@ class TestCompletions:
             top_p=1,
             user="user-1234",
         )
+        completion_stream.response.close()
 
     @parametrize
     def test_raw_response_create_overload_2(self, client: OpenAI) -> None:
@@ -198,8 +222,30 @@ class TestCompletions:
             model="gpt-3.5-turbo",
             stream=True,
         )
+
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-        response.parse()
+        stream = response.parse()
+        stream.close()
+
+    @parametrize
+    def test_streaming_response_create_overload_2(self, client: OpenAI) -> None:
+        with client.chat.completions.with_streaming_response.create(
+            messages=[
+                {
+                    "content": "string",
+                    "role": "system",
+                }
+            ],
+            model="gpt-3.5-turbo",
+            stream=True,
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            stream = response.parse()
+            stream.close()
+
+        assert cast(Any, response.is_closed) is True
 
 
 class TestAsyncCompletions:
@@ -294,13 +340,34 @@ class TestAsyncCompletions:
             ],
             model="gpt-3.5-turbo",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         completion = response.parse()
         assert_matches_type(ChatCompletion, completion, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_create_overload_1(self, client: AsyncOpenAI) -> None:
+        async with client.chat.completions.with_streaming_response.create(
+            messages=[
+                {
+                    "content": "string",
+                    "role": "system",
+                }
+            ],
+            model="gpt-3.5-turbo",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            completion = await response.parse()
+            assert_matches_type(ChatCompletion, completion, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None:
-        await client.chat.completions.create(
+        completion_stream = await client.chat.completions.create(
             messages=[
                 {
                     "content": "string",
@@ -310,10 +377,11 @@ class TestAsyncCompletions:
             model="gpt-3.5-turbo",
             stream=True,
         )
+        await completion_stream.response.aclose()
 
     @parametrize
     async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenAI) -> None:
-        await client.chat.completions.create(
+        completion_stream = await client.chat.completions.create(
             messages=[
                 {
                     "content": "string",
@@ -372,6 +440,7 @@ class TestAsyncCompletions:
             top_p=1,
             user="user-1234",
         )
+        await completion_stream.response.aclose()
 
     @parametrize
     async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None:
@@ -385,5 +454,27 @@ class TestAsyncCompletions:
             model="gpt-3.5-turbo",
             stream=True,
         )
+
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-        response.parse()
+        stream = response.parse()
+        await stream.close()
+
+    @parametrize
+    async def test_streaming_response_create_overload_2(self, client: AsyncOpenAI) -> None:
+        async with client.chat.completions.with_streaming_response.create(
+            messages=[
+                {
+                    "content": "string",
+                    "role": "system",
+                }
+            ],
+            model="gpt-3.5-turbo",
+            stream=True,
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            stream = await response.parse()
+            await stream.close()
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/fine_tuning/test_jobs.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -53,10 +54,26 @@ class TestJobs:
             model="gpt-3.5-turbo",
             training_file="file-abc123",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         job = response.parse()
         assert_matches_type(FineTuningJob, job, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.fine_tuning.jobs.with_streaming_response.create(
+            model="gpt-3.5-turbo",
+            training_file="file-abc123",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            job = response.parse()
+            assert_matches_type(FineTuningJob, job, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_retrieve(self, client: OpenAI) -> None:
         job = client.fine_tuning.jobs.retrieve(
@@ -69,10 +86,25 @@ class TestJobs:
         response = client.fine_tuning.jobs.with_raw_response.retrieve(
             "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         job = response.parse()
         assert_matches_type(FineTuningJob, job, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+        with client.fine_tuning.jobs.with_streaming_response.retrieve(
+            "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            job = response.parse()
+            assert_matches_type(FineTuningJob, job, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_list(self, client: OpenAI) -> None:
         job = client.fine_tuning.jobs.list()
@@ -89,10 +121,23 @@ class TestJobs:
     @parametrize
     def test_raw_response_list(self, client: OpenAI) -> None:
         response = client.fine_tuning.jobs.with_raw_response.list()
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         job = response.parse()
         assert_matches_type(SyncCursorPage[FineTuningJob], job, path=["response"])
 
+    @parametrize
+    def test_streaming_response_list(self, client: OpenAI) -> None:
+        with client.fine_tuning.jobs.with_streaming_response.list() as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            job = response.parse()
+            assert_matches_type(SyncCursorPage[FineTuningJob], job, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_cancel(self, client: OpenAI) -> None:
         job = client.fine_tuning.jobs.cancel(
@@ -105,10 +150,25 @@ class TestJobs:
         response = client.fine_tuning.jobs.with_raw_response.cancel(
             "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         job = response.parse()
         assert_matches_type(FineTuningJob, job, path=["response"])
 
+    @parametrize
+    def test_streaming_response_cancel(self, client: OpenAI) -> None:
+        with client.fine_tuning.jobs.with_streaming_response.cancel(
+            "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            job = response.parse()
+            assert_matches_type(FineTuningJob, job, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_list_events(self, client: OpenAI) -> None:
         job = client.fine_tuning.jobs.list_events(
@@ -130,10 +190,25 @@ class TestJobs:
         response = client.fine_tuning.jobs.with_raw_response.list_events(
             "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         job = response.parse()
         assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"])
 
+    @parametrize
+    def test_streaming_response_list_events(self, client: OpenAI) -> None:
+        with client.fine_tuning.jobs.with_streaming_response.list_events(
+            "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            job = response.parse()
+            assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncJobs:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -169,10 +244,26 @@ class TestAsyncJobs:
             model="gpt-3.5-turbo",
             training_file="file-abc123",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         job = response.parse()
         assert_matches_type(FineTuningJob, job, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.fine_tuning.jobs.with_streaming_response.create(
+            model="gpt-3.5-turbo",
+            training_file="file-abc123",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            job = await response.parse()
+            assert_matches_type(FineTuningJob, job, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
         job = await client.fine_tuning.jobs.retrieve(
@@ -185,10 +276,25 @@ class TestAsyncJobs:
         response = await client.fine_tuning.jobs.with_raw_response.retrieve(
             "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         job = response.parse()
         assert_matches_type(FineTuningJob, job, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None:
+        async with client.fine_tuning.jobs.with_streaming_response.retrieve(
+            "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            job = await response.parse()
+            assert_matches_type(FineTuningJob, job, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_list(self, client: AsyncOpenAI) -> None:
         job = await client.fine_tuning.jobs.list()
@@ -205,10 +311,23 @@ class TestAsyncJobs:
     @parametrize
     async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
         response = await client.fine_tuning.jobs.with_raw_response.list()
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         job = response.parse()
         assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_list(self, client: AsyncOpenAI) -> None:
+        async with client.fine_tuning.jobs.with_streaming_response.list() as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            job = await response.parse()
+            assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_cancel(self, client: AsyncOpenAI) -> None:
         job = await client.fine_tuning.jobs.cancel(
@@ -221,10 +340,25 @@ class TestAsyncJobs:
         response = await client.fine_tuning.jobs.with_raw_response.cancel(
             "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         job = response.parse()
         assert_matches_type(FineTuningJob, job, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_cancel(self, client: AsyncOpenAI) -> None:
+        async with client.fine_tuning.jobs.with_streaming_response.cancel(
+            "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            job = await response.parse()
+            assert_matches_type(FineTuningJob, job, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_list_events(self, client: AsyncOpenAI) -> None:
         job = await client.fine_tuning.jobs.list_events(
@@ -246,6 +380,21 @@ class TestAsyncJobs:
         response = await client.fine_tuning.jobs.with_raw_response.list_events(
             "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         job = response.parse()
         assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_list_events(self, client: AsyncOpenAI) -> None:
+        async with client.fine_tuning.jobs.with_streaming_response.list_events(
+            "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            job = await response.parse()
+            assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/test_completions.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -57,21 +58,38 @@ class TestCompletions:
             model="string",
             prompt="This is a test.",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         completion = response.parse()
         assert_matches_type(Completion, completion, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create_overload_1(self, client: OpenAI) -> None:
+        with client.completions.with_streaming_response.create(
+            model="string",
+            prompt="This is a test.",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            completion = response.parse()
+            assert_matches_type(Completion, completion, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_create_overload_2(self, client: OpenAI) -> None:
-        client.completions.create(
+        completion_stream = client.completions.create(
             model="string",
             prompt="This is a test.",
             stream=True,
         )
+        completion_stream.response.close()
 
     @parametrize
     def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
-        client.completions.create(
+        completion_stream = client.completions.create(
             model="string",
             prompt="This is a test.",
             stream=True,
@@ -90,6 +108,7 @@ class TestCompletions:
             top_p=1,
             user="user-1234",
         )
+        completion_stream.response.close()
 
     @parametrize
     def test_raw_response_create_overload_2(self, client: OpenAI) -> None:
@@ -98,8 +117,25 @@ class TestCompletions:
             prompt="This is a test.",
             stream=True,
         )
+
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-        response.parse()
+        stream = response.parse()
+        stream.close()
+
+    @parametrize
+    def test_streaming_response_create_overload_2(self, client: OpenAI) -> None:
+        with client.completions.with_streaming_response.create(
+            model="string",
+            prompt="This is a test.",
+            stream=True,
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            stream = response.parse()
+            stream.close()
+
+        assert cast(Any, response.is_closed) is True
 
 
 class TestAsyncCompletions:
@@ -144,21 +180,38 @@ class TestAsyncCompletions:
             model="string",
             prompt="This is a test.",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         completion = response.parse()
         assert_matches_type(Completion, completion, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_create_overload_1(self, client: AsyncOpenAI) -> None:
+        async with client.completions.with_streaming_response.create(
+            model="string",
+            prompt="This is a test.",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            completion = await response.parse()
+            assert_matches_type(Completion, completion, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None:
-        await client.completions.create(
+        completion_stream = await client.completions.create(
             model="string",
             prompt="This is a test.",
             stream=True,
         )
+        await completion_stream.response.aclose()
 
     @parametrize
     async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenAI) -> None:
-        await client.completions.create(
+        completion_stream = await client.completions.create(
             model="string",
             prompt="This is a test.",
             stream=True,
@@ -177,6 +230,7 @@ class TestAsyncCompletions:
             top_p=1,
             user="user-1234",
         )
+        await completion_stream.response.aclose()
 
     @parametrize
     async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None:
@@ -185,5 +239,22 @@ class TestAsyncCompletions:
             prompt="This is a test.",
             stream=True,
         )
+
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-        response.parse()
+        stream = response.parse()
+        await stream.close()
+
+    @parametrize
+    async def test_streaming_response_create_overload_2(self, client: AsyncOpenAI) -> None:
+        async with client.completions.with_streaming_response.create(
+            model="string",
+            prompt="This is a test.",
+            stream=True,
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            stream = await response.parse()
+            await stream.close()
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/test_embeddings.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -44,10 +45,26 @@ class TestEmbeddings:
             input="The quick brown fox jumped over the lazy dog",
             model="text-embedding-ada-002",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         embedding = response.parse()
         assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.embeddings.with_streaming_response.create(
+            input="The quick brown fox jumped over the lazy dog",
+            model="text-embedding-ada-002",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            embedding = response.parse()
+            assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncEmbeddings:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -78,6 +95,22 @@ class TestAsyncEmbeddings:
             input="The quick brown fox jumped over the lazy dog",
             model="text-embedding-ada-002",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         embedding = response.parse()
         assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.embeddings.with_streaming_response.create(
+            input="The quick brown fox jumped over the lazy dog",
+            model="text-embedding-ada-002",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            embedding = await response.parse()
+            assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/test_files.py
@@ -3,15 +3,16 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import httpx
 import pytest
 from respx import MockRouter
 
+import openai._legacy_response as _legacy_response
 from openai import OpenAI, AsyncOpenAI
 from tests.utils import assert_matches_type
 from openai.types import FileObject, FileDeleted
-from openai._types import BinaryResponseContent
 from openai._client import OpenAI, AsyncOpenAI
 from openai.pagination import SyncPage, AsyncPage
 
@@ -40,10 +41,26 @@ class TestFiles:
             file=b"raw file contents",
             purpose="fine-tune",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(FileObject, file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.files.with_streaming_response.create(
+            file=b"raw file contents",
+            purpose="fine-tune",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(FileObject, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_retrieve(self, client: OpenAI) -> None:
         file = client.files.retrieve(
@@ -56,10 +73,25 @@ class TestFiles:
         response = client.files.with_raw_response.retrieve(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(FileObject, file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+        with client.files.with_streaming_response.retrieve(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(FileObject, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_list(self, client: OpenAI) -> None:
         file = client.files.list()
@@ -75,10 +107,23 @@ class TestFiles:
     @parametrize
     def test_raw_response_list(self, client: OpenAI) -> None:
         response = client.files.with_raw_response.list()
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(SyncPage[FileObject], file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_list(self, client: OpenAI) -> None:
+        with client.files.with_streaming_response.list() as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(SyncPage[FileObject], file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_delete(self, client: OpenAI) -> None:
         file = client.files.delete(
@@ -91,10 +136,25 @@ class TestFiles:
         response = client.files.with_raw_response.delete(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(FileDeleted, file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_delete(self, client: OpenAI) -> None:
+        with client.files.with_streaming_response.delete(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(FileDeleted, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     @pytest.mark.respx(base_url=base_url)
     def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None:
@@ -102,20 +162,37 @@ class TestFiles:
         file = client.files.content(
             "string",
         )
-        assert isinstance(file, BinaryResponseContent)
+        assert isinstance(file, _legacy_response.HttpxBinaryResponseContent)
         assert file.json() == {"foo": "bar"}
 
     @parametrize
     @pytest.mark.respx(base_url=base_url)
     def test_raw_response_content(self, client: OpenAI, respx_mock: MockRouter) -> None:
         respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
         response = client.files.with_raw_response.content(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
-        assert isinstance(file, BinaryResponseContent)
-        assert file.json() == {"foo": "bar"}
+        assert_matches_type(_legacy_response.HttpxBinaryResponseContent, file, path=["response"])
+
+    @parametrize
+    @pytest.mark.respx(base_url=base_url)
+    def test_streaming_response_content(self, client: OpenAI, respx_mock: MockRouter) -> None:
+        respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        with client.files.with_streaming_response.content(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = response.parse()
+            assert_matches_type(bytes, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
 
     @parametrize
     def test_method_retrieve_content(self, client: OpenAI) -> None:
@@ -123,6 +200,7 @@ class TestFiles:
             file = client.files.retrieve_content(
                 "string",
             )
+
         assert_matches_type(str, file, path=["response"])
 
     @parametrize
@@ -131,10 +209,26 @@ class TestFiles:
             response = client.files.with_raw_response.retrieve_content(
                 "string",
             )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(str, file, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve_content(self, client: OpenAI) -> None:
+        with pytest.warns(DeprecationWarning):
+            with client.files.with_streaming_response.retrieve_content(
+                "string",
+            ) as response:
+                assert not response.is_closed
+                assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+                file = response.parse()
+                assert_matches_type(str, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncFiles:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -155,10 +249,26 @@ class TestAsyncFiles:
             file=b"raw file contents",
             purpose="fine-tune",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(FileObject, file, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.files.with_streaming_response.create(
+            file=b"raw file contents",
+            purpose="fine-tune",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(FileObject, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
         file = await client.files.retrieve(
@@ -171,10 +281,25 @@ class TestAsyncFiles:
         response = await client.files.with_raw_response.retrieve(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(FileObject, file, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None:
+        async with client.files.with_streaming_response.retrieve(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(FileObject, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_list(self, client: AsyncOpenAI) -> None:
         file = await client.files.list()
@@ -190,10 +315,23 @@ class TestAsyncFiles:
     @parametrize
     async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
         response = await client.files.with_raw_response.list()
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(AsyncPage[FileObject], file, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_list(self, client: AsyncOpenAI) -> None:
+        async with client.files.with_streaming_response.list() as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(AsyncPage[FileObject], file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_delete(self, client: AsyncOpenAI) -> None:
         file = await client.files.delete(
@@ -206,10 +344,25 @@ class TestAsyncFiles:
         response = await client.files.with_raw_response.delete(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(FileDeleted, file, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None:
+        async with client.files.with_streaming_response.delete(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(FileDeleted, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     @pytest.mark.respx(base_url=base_url)
     async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None:
@@ -217,20 +370,37 @@ class TestAsyncFiles:
         file = await client.files.content(
             "string",
         )
-        assert isinstance(file, BinaryResponseContent)
+        assert isinstance(file, _legacy_response.HttpxBinaryResponseContent)
         assert file.json() == {"foo": "bar"}
 
     @parametrize
     @pytest.mark.respx(base_url=base_url)
     async def test_raw_response_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None:
         respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
         response = await client.files.with_raw_response.content(
             "string",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
-        assert isinstance(file, BinaryResponseContent)
-        assert file.json() == {"foo": "bar"}
+        assert_matches_type(_legacy_response.HttpxBinaryResponseContent, file, path=["response"])
+
+    @parametrize
+    @pytest.mark.respx(base_url=base_url)
+    async def test_streaming_response_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None:
+        respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        async with client.files.with_streaming_response.content(
+            "string",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            file = await response.parse()
+            assert_matches_type(bytes, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
 
     @parametrize
     async def test_method_retrieve_content(self, client: AsyncOpenAI) -> None:
@@ -238,6 +408,7 @@ class TestAsyncFiles:
             file = await client.files.retrieve_content(
                 "string",
             )
+
         assert_matches_type(str, file, path=["response"])
 
     @parametrize
@@ -246,6 +417,22 @@ class TestAsyncFiles:
             response = await client.files.with_raw_response.retrieve_content(
                 "string",
             )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
         assert_matches_type(str, file, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_retrieve_content(self, client: AsyncOpenAI) -> None:
+        with pytest.warns(DeprecationWarning):
+            async with client.files.with_streaming_response.retrieve_content(
+                "string",
+            ) as response:
+                assert not response.is_closed
+                assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+                file = await response.parse()
+                assert_matches_type(str, file, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/test_images.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -44,10 +45,25 @@ class TestImages:
         response = client.images.with_raw_response.create_variation(
             image=b"raw file contents",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         image = response.parse()
         assert_matches_type(ImagesResponse, image, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create_variation(self, client: OpenAI) -> None:
+        with client.images.with_streaming_response.create_variation(
+            image=b"raw file contents",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            image = response.parse()
+            assert_matches_type(ImagesResponse, image, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_edit(self, client: OpenAI) -> None:
         image = client.images.edit(
@@ -76,10 +92,26 @@ class TestImages:
             image=b"raw file contents",
             prompt="A cute baby sea otter wearing a beret",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         image = response.parse()
         assert_matches_type(ImagesResponse, image, path=["response"])
 
+    @parametrize
+    def test_streaming_response_edit(self, client: OpenAI) -> None:
+        with client.images.with_streaming_response.edit(
+            image=b"raw file contents",
+            prompt="A cute baby sea otter wearing a beret",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            image = response.parse()
+            assert_matches_type(ImagesResponse, image, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_generate(self, client: OpenAI) -> None:
         image = client.images.generate(
@@ -106,10 +138,25 @@ class TestImages:
         response = client.images.with_raw_response.generate(
             prompt="A cute baby sea otter",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         image = response.parse()
         assert_matches_type(ImagesResponse, image, path=["response"])
 
+    @parametrize
+    def test_streaming_response_generate(self, client: OpenAI) -> None:
+        with client.images.with_streaming_response.generate(
+            prompt="A cute baby sea otter",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            image = response.parse()
+            assert_matches_type(ImagesResponse, image, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncImages:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -140,10 +187,25 @@ class TestAsyncImages:
         response = await client.images.with_raw_response.create_variation(
             image=b"raw file contents",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         image = response.parse()
         assert_matches_type(ImagesResponse, image, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_create_variation(self, client: AsyncOpenAI) -> None:
+        async with client.images.with_streaming_response.create_variation(
+            image=b"raw file contents",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            image = await response.parse()
+            assert_matches_type(ImagesResponse, image, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_edit(self, client: AsyncOpenAI) -> None:
         image = await client.images.edit(
@@ -172,10 +234,26 @@ class TestAsyncImages:
             image=b"raw file contents",
             prompt="A cute baby sea otter wearing a beret",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         image = response.parse()
         assert_matches_type(ImagesResponse, image, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_edit(self, client: AsyncOpenAI) -> None:
+        async with client.images.with_streaming_response.edit(
+            image=b"raw file contents",
+            prompt="A cute baby sea otter wearing a beret",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            image = await response.parse()
+            assert_matches_type(ImagesResponse, image, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_generate(self, client: AsyncOpenAI) -> None:
         image = await client.images.generate(
@@ -202,6 +280,21 @@ class TestAsyncImages:
         response = await client.images.with_raw_response.generate(
             prompt="A cute baby sea otter",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         image = response.parse()
         assert_matches_type(ImagesResponse, image, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_generate(self, client: AsyncOpenAI) -> None:
+        async with client.images.with_streaming_response.generate(
+            prompt="A cute baby sea otter",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            image = await response.parse()
+            assert_matches_type(ImagesResponse, image, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/test_models.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -33,10 +34,25 @@ class TestModels:
         response = client.models.with_raw_response.retrieve(
             "gpt-3.5-turbo",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         model = response.parse()
         assert_matches_type(Model, model, path=["response"])
 
+    @parametrize
+    def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+        with client.models.with_streaming_response.retrieve(
+            "gpt-3.5-turbo",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            model = response.parse()
+            assert_matches_type(Model, model, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_list(self, client: OpenAI) -> None:
         model = client.models.list()
@@ -45,10 +61,23 @@ class TestModels:
     @parametrize
     def test_raw_response_list(self, client: OpenAI) -> None:
         response = client.models.with_raw_response.list()
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         model = response.parse()
         assert_matches_type(SyncPage[Model], model, path=["response"])
 
+    @parametrize
+    def test_streaming_response_list(self, client: OpenAI) -> None:
+        with client.models.with_streaming_response.list() as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            model = response.parse()
+            assert_matches_type(SyncPage[Model], model, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     def test_method_delete(self, client: OpenAI) -> None:
         model = client.models.delete(
@@ -61,10 +90,25 @@ class TestModels:
         response = client.models.with_raw_response.delete(
             "ft:gpt-3.5-turbo:acemeco:suffix:abc123",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         model = response.parse()
         assert_matches_type(ModelDeleted, model, path=["response"])
 
+    @parametrize
+    def test_streaming_response_delete(self, client: OpenAI) -> None:
+        with client.models.with_streaming_response.delete(
+            "ft:gpt-3.5-turbo:acemeco:suffix:abc123",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            model = response.parse()
+            assert_matches_type(ModelDeleted, model, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncModels:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -83,10 +127,25 @@ class TestAsyncModels:
         response = await client.models.with_raw_response.retrieve(
             "gpt-3.5-turbo",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         model = response.parse()
         assert_matches_type(Model, model, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None:
+        async with client.models.with_streaming_response.retrieve(
+            "gpt-3.5-turbo",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            model = await response.parse()
+            assert_matches_type(Model, model, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_list(self, client: AsyncOpenAI) -> None:
         model = await client.models.list()
@@ -95,10 +154,23 @@ class TestAsyncModels:
     @parametrize
     async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
         response = await client.models.with_raw_response.list()
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         model = response.parse()
         assert_matches_type(AsyncPage[Model], model, path=["response"])
 
+    @parametrize
+    async def test_streaming_response_list(self, client: AsyncOpenAI) -> None:
+        async with client.models.with_streaming_response.list() as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            model = await response.parse()
+            assert_matches_type(AsyncPage[Model], model, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
     @parametrize
     async def test_method_delete(self, client: AsyncOpenAI) -> None:
         model = await client.models.delete(
@@ -111,6 +183,21 @@ class TestAsyncModels:
         response = await client.models.with_raw_response.delete(
             "ft:gpt-3.5-turbo:acemeco:suffix:abc123",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         model = response.parse()
         assert_matches_type(ModelDeleted, model, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None:
+        async with client.models.with_streaming_response.delete(
+            "ft:gpt-3.5-turbo:acemeco:suffix:abc123",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            model = await response.parse()
+            assert_matches_type(ModelDeleted, model, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/api_resources/test_moderations.py
@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import os
+from typing import Any, cast
 
 import pytest
 
@@ -40,10 +41,25 @@ class TestModerations:
         response = client.moderations.with_raw_response.create(
             input="I want to kill them.",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         moderation = response.parse()
         assert_matches_type(ModerationCreateResponse, moderation, path=["response"])
 
+    @parametrize
+    def test_streaming_response_create(self, client: OpenAI) -> None:
+        with client.moderations.with_streaming_response.create(
+            input="I want to kill them.",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            moderation = response.parse()
+            assert_matches_type(ModerationCreateResponse, moderation, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
+
 
 class TestAsyncModerations:
     strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -70,6 +86,21 @@ class TestAsyncModerations:
         response = await client.moderations.with_raw_response.create(
             input="I want to kill them.",
         )
+
+        assert response.is_closed is True
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         moderation = response.parse()
         assert_matches_type(ModerationCreateResponse, moderation, path=["response"])
+
+    @parametrize
+    async def test_streaming_response_create(self, client: AsyncOpenAI) -> None:
+        async with client.moderations.with_streaming_response.create(
+            input="I want to kill them.",
+        ) as response:
+            assert not response.is_closed
+            assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+            moderation = await response.parse()
+            assert_matches_type(ModerationCreateResponse, moderation, path=["response"])
+
+        assert cast(Any, response.is_closed) is True
tests/test_client.py
@@ -19,6 +19,8 @@ from pydantic import ValidationError
 from openai import OpenAI, AsyncOpenAI, APIResponseValidationError
 from openai._client import OpenAI, AsyncOpenAI
 from openai._models import BaseModel, FinalRequestOptions
+from openai._response import APIResponse, AsyncAPIResponse
+from openai._constants import RAW_RESPONSE_HEADER
 from openai._streaming import Stream, AsyncStream
 from openai._exceptions import OpenAIError, APIStatusError, APITimeoutError, APIResponseValidationError
 from openai._base_client import DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, make_request_options
@@ -220,6 +222,7 @@ class TestOpenAI:
                         # to_raw_response_wrapper leaks through the @functools.wraps() decorator.
                         #
                         # removing the decorator fixes the leak for reasons we don't understand.
+                        "openai/_legacy_response.py",
                         "openai/_response.py",
                         # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason.
                         "openai/_compat.py",
@@ -612,8 +615,9 @@ class TestOpenAI:
 
         respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
 
-        response = self.client.post("/foo", cast_to=Model, stream=True)
-        assert isinstance(response, Stream)
+        stream = self.client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model])
+        assert isinstance(stream, Stream)
+        stream.response.close()
 
     @pytest.mark.respx(base_url=base_url)
     def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
@@ -661,6 +665,33 @@ class TestOpenAI:
         calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
         assert calculated == pytest.approx(timeout, 0.5 * 0.875)  # pyright: ignore[reportUnknownMemberType]
 
+    @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
+    @pytest.mark.respx(base_url=base_url)
+    def test_streaming_response(self) -> None:
+        response = self.client.post(
+            "/chat/completions",
+            body=dict(
+                messages=[
+                    {
+                        "role": "user",
+                        "content": "Say this is a test",
+                    }
+                ],
+                model="gpt-3.5-turbo",
+            ),
+            cast_to=APIResponse[bytes],
+            options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
+        )
+
+        assert not cast(Any, response.is_closed)
+        assert _get_open_connections(self.client) == 1
+
+        for _ in response.iter_bytes():
+            ...
+
+        assert cast(Any, response.is_closed)
+        assert _get_open_connections(self.client) == 0
+
     @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
     @pytest.mark.respx(base_url=base_url)
     def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
@@ -679,7 +710,7 @@ class TestOpenAI:
                     model="gpt-3.5-turbo",
                 ),
                 cast_to=httpx.Response,
-                options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}},
+                options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
             )
 
         assert _get_open_connections(self.client) == 0
@@ -702,7 +733,7 @@ class TestOpenAI:
                     model="gpt-3.5-turbo",
                 ),
                 cast_to=httpx.Response,
-                options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}},
+                options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
             )
 
         assert _get_open_connections(self.client) == 0
@@ -883,6 +914,7 @@ class TestAsyncOpenAI:
                         # to_raw_response_wrapper leaks through the @functools.wraps() decorator.
                         #
                         # removing the decorator fixes the leak for reasons we don't understand.
+                        "openai/_legacy_response.py",
                         "openai/_response.py",
                         # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason.
                         "openai/_compat.py",
@@ -1288,8 +1320,9 @@ class TestAsyncOpenAI:
 
         respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
 
-        response = await self.client.post("/foo", cast_to=Model, stream=True)
-        assert isinstance(response, AsyncStream)
+        stream = await self.client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model])
+        assert isinstance(stream, AsyncStream)
+        await stream.response.aclose()
 
     @pytest.mark.respx(base_url=base_url)
     @pytest.mark.asyncio
@@ -1339,6 +1372,33 @@ class TestAsyncOpenAI:
         calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
         assert calculated == pytest.approx(timeout, 0.5 * 0.875)  # pyright: ignore[reportUnknownMemberType]
 
+    @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
+    @pytest.mark.respx(base_url=base_url)
+    async def test_streaming_response(self) -> None:
+        response = await self.client.post(
+            "/chat/completions",
+            body=dict(
+                messages=[
+                    {
+                        "role": "user",
+                        "content": "Say this is a test",
+                    }
+                ],
+                model="gpt-3.5-turbo",
+            ),
+            cast_to=AsyncAPIResponse[bytes],
+            options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
+        )
+
+        assert not cast(Any, response.is_closed)
+        assert _get_open_connections(self.client) == 1
+
+        async for _ in response.iter_bytes():
+            ...
+
+        assert cast(Any, response.is_closed)
+        assert _get_open_connections(self.client) == 0
+
     @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
     @pytest.mark.respx(base_url=base_url)
     async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
@@ -1357,7 +1417,7 @@ class TestAsyncOpenAI:
                     model="gpt-3.5-turbo",
                 ),
                 cast_to=httpx.Response,
-                options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}},
+                options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
             )
 
         assert _get_open_connections(self.client) == 0
@@ -1380,7 +1440,7 @@ class TestAsyncOpenAI:
                     model="gpt-3.5-turbo",
                 ),
                 cast_to=httpx.Response,
-                options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}},
+                options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
             )
 
         assert _get_open_connections(self.client) == 0
tests/test_response.py
@@ -0,0 +1,50 @@
+from typing import List
+
+import httpx
+import pytest
+
+from openai._response import (
+    APIResponse,
+    BaseAPIResponse,
+    AsyncAPIResponse,
+    BinaryAPIResponse,
+    AsyncBinaryAPIResponse,
+    extract_response_type,
+)
+
+
+class ConcreteBaseAPIResponse(APIResponse[bytes]):
+    ...
+
+
+class ConcreteAPIResponse(APIResponse[List[str]]):
+    ...
+
+
+class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]):
+    ...
+
+
+def test_extract_response_type_direct_classes() -> None:
+    assert extract_response_type(BaseAPIResponse[str]) == str
+    assert extract_response_type(APIResponse[str]) == str
+    assert extract_response_type(AsyncAPIResponse[str]) == str
+
+
+def test_extract_response_type_direct_class_missing_type_arg() -> None:
+    with pytest.raises(
+        RuntimeError,
+        match="Expected type <class 'openai._response.AsyncAPIResponse'> to have a type argument at index 0 but it did not",
+    ):
+        extract_response_type(AsyncAPIResponse)
+
+
+def test_extract_response_type_concrete_subclasses() -> None:
+    assert extract_response_type(ConcreteBaseAPIResponse) == bytes
+    assert extract_response_type(ConcreteAPIResponse) == List[str]
+    assert extract_response_type(ConcreteAsyncAPIResponse) == httpx.Response
+
+
+def test_extract_response_type_binary_response() -> None:
+    assert extract_response_type(BinaryAPIResponse) == bytes
+    assert extract_response_type(AsyncBinaryAPIResponse) == bytes
tests/utils.py
@@ -1,6 +1,7 @@
 from __future__ import annotations
 
 import os
+import inspect
 import traceback
 import contextlib
 from typing import Any, TypeVar, Iterator, cast
@@ -68,6 +69,8 @@ def assert_matches_type(
         assert isinstance(value, bool)
     elif origin == float:
         assert isinstance(value, float)
+    elif origin == bytes:
+        assert isinstance(value, bytes)
     elif origin == datetime:
         assert isinstance(value, datetime)
     elif origin == date:
@@ -100,6 +103,8 @@ def assert_matches_type(
     elif issubclass(origin, BaseModel):
         assert isinstance(value, type_)
         assert assert_matches_model(type_, cast(Any, value), path=path)
+    elif inspect.isclass(origin) and origin.__name__ == "HttpxBinaryResponseContent":
+        assert value.__class__.__name__ == "HttpxBinaryResponseContent"
     else:
         assert None, f"Unhandled field type: {type_}"
 
README.md
@@ -414,7 +414,7 @@ if response.my_field is None:
 
 ### Accessing raw response data (e.g. headers)
 
-The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call.
+The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,
 
 ```py
 from openai import OpenAI
@@ -433,7 +433,40 @@ completion = response.parse()  # get the object that `chat.completions.create()`
 print(completion)
 ```
 
-These methods return an [`APIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object.
+These methods return an [`LegacyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version.
+
+For the sync client this will mostly be the same with the exception
+of `content` & `text` will be methods instead of properties. In the
+async client, all methods will be async.
+
+A migration script will be provided & the migration in general should
+be smooth.
+
+#### `.with_streaming_response`
+
+The above interface eagerly reads the full response body when you make the request, which may not always be what you want.
+
+To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.
+
+As such, `.with_streaming_response` methods return a different [`APIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object, and the async client returns an [`AsyncAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object.
+
+```python
+with client.chat.completions.with_streaming_response.create(
+    messages=[
+        {
+            "role": "user",
+            "content": "Say this is a test",
+        }
+    ],
+    model="gpt-3.5-turbo",
+) as response:
+    print(response.headers.get("X-My-Header"))
+
+    for line in response.iter_lines():
+        print(line)
+```
+
+The context manager is required so that the response will reliably be closed.
 
 ### Configuring the HTTP client