diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 43515db369..b11d9e721d 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "1.50.1"
+ ".": "1.50.2"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 0998368a4c..68789976bf 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,2 +1,2 @@
configured_endpoints: 68
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-17ddd746c775ca4d4fbe64e5621ac30756ef09c061ff6313190b6ec162222d4c.yml
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-71e58a77027c67e003fdd1b1ac8ac11557d8bfabc7666d1a827c6b1ca8ab98b5.yml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c51de0481b..37571051de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 1.50.2 (2024-09-27)
+
+Full Changelog: [v1.50.1...v1.50.2](https://github.com/openai/openai-python/compare/v1.50.1...v1.50.2)
+
+### Bug Fixes
+
+* **audio:** correct types for transcriptions / translations ([#1755](https://github.com/openai/openai-python/issues/1755)) ([76c1f3f](https://github.com/openai/openai-python/commit/76c1f3f318b68003aae124c02efc4547a398a864))
+
## 1.50.1 (2024-09-27)
Full Changelog: [v1.50.0...v1.50.1](https://github.com/openai/openai-python/compare/v1.50.0...v1.50.1)
diff --git a/api.md b/api.md
index 29db54f0fd..813c5cb615 100644
--- a/api.md
+++ b/api.md
@@ -122,24 +122,30 @@ from openai.types import AudioModel, AudioResponseFormat
Types:
```python
-from openai.types.audio import Transcription
+from openai.types.audio import (
+ Transcription,
+ TranscriptionSegment,
+ TranscriptionVerbose,
+ TranscriptionWord,
+ TranscriptionCreateResponse,
+)
```
Methods:
-- client.audio.transcriptions.create(\*\*params) -> Transcription
+- client.audio.transcriptions.create(\*\*params) -> TranscriptionCreateResponse
## Translations
Types:
```python
-from openai.types.audio import Translation
+from openai.types.audio import Translation, TranslationVerbose, TranslationCreateResponse
```
Methods:
-- client.audio.translations.create(\*\*params) -> Translation
+- client.audio.translations.create(\*\*params) -> TranslationCreateResponse
## Speech
diff --git a/pyproject.toml b/pyproject.toml
index 7ebf741165..90b8c8518c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "1.50.1"
+version = "1.50.2"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_utils/_reflection.py b/src/openai/_utils/_reflection.py
index 89aa712ac4..bdaca29e4a 100644
--- a/src/openai/_utils/_reflection.py
+++ b/src/openai/_utils/_reflection.py
@@ -15,6 +15,7 @@ def assert_signatures_in_sync(
check_func: Callable[..., Any],
*,
exclude_params: set[str] = set(),
+ description: str = "",
) -> None:
"""Ensure that the signature of the second function matches the first."""
@@ -39,4 +40,6 @@ def assert_signatures_in_sync(
continue
if errors:
- raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors))
+ raise AssertionError(
+ f"{len(errors)} errors encountered when comparing signatures{description}:\n\n" + "\n\n".join(errors)
+ )
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 2b0fd31d26..641dd21648 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "openai"
-__version__ = "1.50.1" # x-release-please-version
+__version__ = "1.50.2" # x-release-please-version
diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py
index fd042d1ac3..e6596a480e 100644
--- a/src/openai/resources/audio/transcriptions.py
+++ b/src/openai/resources/audio/transcriptions.py
@@ -2,8 +2,9 @@
from __future__ import annotations
-from typing import List, Union, Mapping, cast
-from typing_extensions import Literal
+import logging
+from typing import TYPE_CHECKING, List, Union, Mapping, cast
+from typing_extensions import Literal, overload, assert_never
import httpx
@@ -24,9 +25,12 @@
from ...types.audio_model import AudioModel
from ...types.audio.transcription import Transcription
from ...types.audio_response_format import AudioResponseFormat
+from ...types.audio.transcription_verbose import TranscriptionVerbose
__all__ = ["Transcriptions", "AsyncTranscriptions"]
+log: logging.Logger = logging.getLogger("openai.audio.transcriptions")
+
class Transcriptions(SyncAPIResource):
@cached_property
@@ -48,14 +52,53 @@ def with_streaming_response(self) -> TranscriptionsWithStreamingResponse:
"""
return TranscriptionsWithStreamingResponse(self)
+ @overload
+ def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN,
+ language: str | NotGiven = NOT_GIVEN,
+ prompt: str | NotGiven = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> Transcription: ...
+
+ @overload
+ def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ response_format: Literal["verbose_json"],
+ language: str | NotGiven = NOT_GIVEN,
+ prompt: str | NotGiven = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> TranscriptionVerbose: ...
+
+ @overload
def create(
self,
*,
file: FileTypes,
model: Union[str, AudioModel],
+ response_format: Literal["text", "srt", "vtt"],
language: str | NotGiven = NOT_GIVEN,
prompt: str | NotGiven = NOT_GIVEN,
- response_format: AudioResponseFormat | NotGiven = NOT_GIVEN,
temperature: float | NotGiven = NOT_GIVEN,
timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -64,7 +107,25 @@ def create(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Transcription:
+ ) -> str: ...
+
+ def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ language: str | NotGiven = NOT_GIVEN,
+ prompt: str | NotGiven = NOT_GIVEN,
+ response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> Transcription | TranscriptionVerbose | str:
"""
Transcribes audio into the input language.
@@ -124,14 +185,14 @@ def create(
# sent to the server will contain a `boundary` parameter, e.g.
# multipart/form-data; boundary=---abc--
extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
- return self._post(
+ return self._post( # type: ignore[return-value]
"/audio/transcriptions",
body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams),
files=files,
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Transcription,
+ cast_to=_get_response_format_type(response_format),
)
@@ -155,14 +216,15 @@ def with_streaming_response(self) -> AsyncTranscriptionsWithStreamingResponse:
"""
return AsyncTranscriptionsWithStreamingResponse(self)
+ @overload
async def create(
self,
*,
file: FileTypes,
model: Union[str, AudioModel],
+ response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN,
language: str | NotGiven = NOT_GIVEN,
prompt: str | NotGiven = NOT_GIVEN,
- response_format: AudioResponseFormat | NotGiven = NOT_GIVEN,
temperature: float | NotGiven = NOT_GIVEN,
timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -171,7 +233,63 @@ async def create(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Transcription:
+ ) -> Transcription: ...
+
+ @overload
+ async def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ response_format: Literal["verbose_json"],
+ language: str | NotGiven = NOT_GIVEN,
+ prompt: str | NotGiven = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> TranscriptionVerbose: ...
+
+ @overload
+ async def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ response_format: Literal["text", "srt", "vtt"],
+ language: str | NotGiven = NOT_GIVEN,
+ prompt: str | NotGiven = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> str: ...
+
+ async def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ language: str | NotGiven = NOT_GIVEN,
+ prompt: str | NotGiven = NOT_GIVEN,
+ response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> Transcription | TranscriptionVerbose | str:
"""
Transcribes audio into the input language.
@@ -238,7 +356,7 @@ async def create(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Transcription,
+ cast_to=_get_response_format_type(response_format),
)
@@ -276,3 +394,22 @@ def __init__(self, transcriptions: AsyncTranscriptions) -> None:
self.create = async_to_streamed_response_wrapper(
transcriptions.create,
)
+
+
+def _get_response_format_type(
+ response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven,
+) -> type[Transcription | TranscriptionVerbose | str]:
+ if isinstance(response_format, NotGiven) or response_format is None: # pyright: ignore[reportUnnecessaryComparison]
+ return Transcription
+
+ if response_format == "json":
+ return Transcription
+ elif response_format == "verbose_json":
+ return TranscriptionVerbose
+ elif response_format == "srt" or response_format == "text" or response_format == "vtt":
+ return str
+ elif TYPE_CHECKING: # type: ignore[unreachable]
+ assert_never(response_format)
+ else:
+ log.warn("Unexpected audio response format: %s", response_format)
+ return Transcription
diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py
index fe08dd550e..53ab625873 100644
--- a/src/openai/resources/audio/translations.py
+++ b/src/openai/resources/audio/translations.py
@@ -2,7 +2,9 @@
from __future__ import annotations
-from typing import Union, Mapping, cast
+import logging
+from typing import TYPE_CHECKING, Union, Mapping, cast
+from typing_extensions import Literal, overload, assert_never
import httpx
@@ -23,9 +25,12 @@
from ...types.audio_model import AudioModel
from ...types.audio.translation import Translation
from ...types.audio_response_format import AudioResponseFormat
+from ...types.audio.translation_verbose import TranslationVerbose
__all__ = ["Translations", "AsyncTranslations"]
+log: logging.Logger = logging.getLogger("openai.audio.transcriptions")
+
class Translations(SyncAPIResource):
@cached_property
@@ -47,13 +52,64 @@ def with_streaming_response(self) -> TranslationsWithStreamingResponse:
"""
return TranslationsWithStreamingResponse(self)
+ @overload
+ def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN,
+ prompt: str | NotGiven = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> Translation: ...
+
+ @overload
+ def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ response_format: Literal["verbose_json"],
+ prompt: str | NotGiven = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> TranslationVerbose: ...
+
+ @overload
+ def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ response_format: Literal["text", "srt", "vtt"],
+ prompt: str | NotGiven = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> str: ...
+
def create(
self,
*,
file: FileTypes,
model: Union[str, AudioModel],
prompt: str | NotGiven = NOT_GIVEN,
- response_format: AudioResponseFormat | NotGiven = NOT_GIVEN,
+ response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN,
temperature: float | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -61,7 +117,7 @@ def create(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Translation:
+ ) -> Translation | TranslationVerbose | str:
"""
Translates audio into English.
@@ -108,14 +164,14 @@ def create(
# sent to the server will contain a `boundary` parameter, e.g.
# multipart/form-data; boundary=---abc--
extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
- return self._post(
+ return self._post( # type: ignore[return-value]
"/audio/translations",
body=maybe_transform(body, translation_create_params.TranslationCreateParams),
files=files,
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Translation,
+ cast_to=_get_response_format_type(response_format),
)
@@ -139,13 +195,14 @@ def with_streaming_response(self) -> AsyncTranslationsWithStreamingResponse:
"""
return AsyncTranslationsWithStreamingResponse(self)
+ @overload
async def create(
self,
*,
file: FileTypes,
model: Union[str, AudioModel],
+ response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN,
prompt: str | NotGiven = NOT_GIVEN,
- response_format: AudioResponseFormat | NotGiven = NOT_GIVEN,
temperature: float | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -153,7 +210,57 @@ async def create(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Translation:
+ ) -> Translation: ...
+
+ @overload
+ async def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ response_format: Literal["verbose_json"],
+ prompt: str | NotGiven = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> TranslationVerbose: ...
+
+ @overload
+ async def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ response_format: Literal["text", "srt", "vtt"],
+ prompt: str | NotGiven = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> str: ...
+
+ async def create(
+ self,
+ *,
+ file: FileTypes,
+ model: Union[str, AudioModel],
+ prompt: str | NotGiven = NOT_GIVEN,
+ response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN,
+ temperature: float | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> Translation | TranslationVerbose | str:
"""
Translates audio into English.
@@ -207,7 +314,7 @@ async def create(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Translation,
+ cast_to=_get_response_format_type(response_format),
)
@@ -245,3 +352,22 @@ def __init__(self, translations: AsyncTranslations) -> None:
self.create = async_to_streamed_response_wrapper(
translations.create,
)
+
+
+def _get_response_format_type(
+ response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven,
+) -> type[Translation | TranslationVerbose | str]:
+ if isinstance(response_format, NotGiven) or response_format is None: # pyright: ignore[reportUnnecessaryComparison]
+ return Translation
+
+ if response_format == "json":
+ return Translation
+ elif response_format == "verbose_json":
+ return TranslationVerbose
+ elif response_format == "srt" or response_format == "text" or response_format == "vtt":
+ return str
+ elif TYPE_CHECKING: # type: ignore[unreachable]
+ assert_never(response_format)
+ else:
+ log.warn("Unexpected audio response format: %s", response_format)
+ return Transcription
diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py
index 1de5c0ff82..822e0f3a8d 100644
--- a/src/openai/types/audio/__init__.py
+++ b/src/openai/types/audio/__init__.py
@@ -5,6 +5,12 @@
from .translation import Translation as Translation
from .speech_model import SpeechModel as SpeechModel
from .transcription import Transcription as Transcription
+from .transcription_word import TranscriptionWord as TranscriptionWord
+from .translation_verbose import TranslationVerbose as TranslationVerbose
from .speech_create_params import SpeechCreateParams as SpeechCreateParams
+from .transcription_segment import TranscriptionSegment as TranscriptionSegment
+from .transcription_verbose import TranscriptionVerbose as TranscriptionVerbose
from .translation_create_params import TranslationCreateParams as TranslationCreateParams
from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams
+from .translation_create_response import TranslationCreateResponse as TranslationCreateResponse
+from .transcription_create_response import TranscriptionCreateResponse as TranscriptionCreateResponse
diff --git a/src/openai/types/audio/transcription_create_response.py b/src/openai/types/audio/transcription_create_response.py
new file mode 100644
index 0000000000..2f7bed8114
--- /dev/null
+++ b/src/openai/types/audio/transcription_create_response.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import TypeAlias
+
+from .transcription import Transcription
+from .transcription_verbose import TranscriptionVerbose
+
+__all__ = ["TranscriptionCreateResponse"]
+
+TranscriptionCreateResponse: TypeAlias = Union[Transcription, TranscriptionVerbose]
diff --git a/src/openai/types/audio/transcription_segment.py b/src/openai/types/audio/transcription_segment.py
new file mode 100644
index 0000000000..522c401ebb
--- /dev/null
+++ b/src/openai/types/audio/transcription_segment.py
@@ -0,0 +1,49 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from ..._models import BaseModel
+
+__all__ = ["TranscriptionSegment"]
+
+
+class TranscriptionSegment(BaseModel):
+ id: int
+ """Unique identifier of the segment."""
+
+ avg_logprob: float
+ """Average logprob of the segment.
+
+ If the value is lower than -1, consider the logprobs failed.
+ """
+
+ compression_ratio: float
+ """Compression ratio of the segment.
+
+ If the value is greater than 2.4, consider the compression failed.
+ """
+
+ end: float
+ """End time of the segment in seconds."""
+
+ no_speech_prob: float
+ """Probability of no speech in the segment.
+
+ If the value is higher than 1.0 and the `avg_logprob` is below -1, consider this
+ segment silent.
+ """
+
+ seek: int
+ """Seek offset of the segment."""
+
+ start: float
+ """Start time of the segment in seconds."""
+
+ temperature: float
+ """Temperature parameter used for generating the segment."""
+
+ text: str
+ """Text content of the segment."""
+
+ tokens: List[int]
+ """Array of token IDs for the text content."""
diff --git a/src/openai/types/audio/transcription_verbose.py b/src/openai/types/audio/transcription_verbose.py
new file mode 100644
index 0000000000..3b18fa4871
--- /dev/null
+++ b/src/openai/types/audio/transcription_verbose.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from ..._models import BaseModel
+from .transcription_word import TranscriptionWord
+from .transcription_segment import TranscriptionSegment
+
+__all__ = ["TranscriptionVerbose"]
+
+
+class TranscriptionVerbose(BaseModel):
+ duration: str
+ """The duration of the input audio."""
+
+ language: str
+ """The language of the input audio."""
+
+ text: str
+ """The transcribed text."""
+
+ segments: Optional[List[TranscriptionSegment]] = None
+ """Segments of the transcribed text and their corresponding details."""
+
+ words: Optional[List[TranscriptionWord]] = None
+ """Extracted words and their corresponding timestamps."""
diff --git a/src/openai/types/audio/transcription_word.py b/src/openai/types/audio/transcription_word.py
new file mode 100644
index 0000000000..969da32509
--- /dev/null
+++ b/src/openai/types/audio/transcription_word.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+
+from ..._models import BaseModel
+
+__all__ = ["TranscriptionWord"]
+
+
+class TranscriptionWord(BaseModel):
+ end: float
+ """End time of the word in seconds."""
+
+ start: float
+ """Start time of the word in seconds."""
+
+ word: str
+ """The text content of the word."""
diff --git a/src/openai/types/audio/translation_create_response.py b/src/openai/types/audio/translation_create_response.py
new file mode 100644
index 0000000000..9953813c08
--- /dev/null
+++ b/src/openai/types/audio/translation_create_response.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import TypeAlias
+
+from .translation import Translation
+from .translation_verbose import TranslationVerbose
+
+__all__ = ["TranslationCreateResponse"]
+
+TranslationCreateResponse: TypeAlias = Union[Translation, TranslationVerbose]
diff --git a/src/openai/types/audio/translation_verbose.py b/src/openai/types/audio/translation_verbose.py
new file mode 100644
index 0000000000..5901ae7535
--- /dev/null
+++ b/src/openai/types/audio/translation_verbose.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from ..._models import BaseModel
+from .transcription_segment import TranscriptionSegment
+
+__all__ = ["TranslationVerbose"]
+
+
+class TranslationVerbose(BaseModel):
+ duration: str
+ """The duration of the input audio."""
+
+ language: str
+ """The language of the output translation (always `english`)."""
+
+ text: str
+ """The translated text."""
+
+ segments: Optional[List[TranscriptionSegment]] = None
+ """Segments of the translated text and their corresponding details."""
diff --git a/src/openai/types/beta/static_file_chunking_strategy.py b/src/openai/types/beta/static_file_chunking_strategy.py
index ba80e1a2b9..6080093517 100644
--- a/src/openai/types/beta/static_file_chunking_strategy.py
+++ b/src/openai/types/beta/static_file_chunking_strategy.py
@@ -1,7 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from ..._models import BaseModel
__all__ = ["StaticFileChunkingStrategy"]
diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py
index ba8e9e4099..0fa91eb152 100644
--- a/tests/api_resources/audio/test_transcriptions.py
+++ b/tests/api_resources/audio/test_transcriptions.py
@@ -9,7 +9,7 @@
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
-from openai.types.audio import Transcription
+from openai.types.audio import TranscriptionCreateResponse
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -23,7 +23,7 @@ def test_method_create(self, client: OpenAI) -> None:
file=b"raw file contents",
model="whisper-1",
)
- assert_matches_type(Transcription, transcription, path=["response"])
+ assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"])
@parametrize
def test_method_create_with_all_params(self, client: OpenAI) -> None:
@@ -36,7 +36,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None:
temperature=0,
timestamp_granularities=["word", "segment"],
)
- assert_matches_type(Transcription, transcription, path=["response"])
+ assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"])
@parametrize
def test_raw_response_create(self, client: OpenAI) -> None:
@@ -48,7 +48,7 @@ def test_raw_response_create(self, client: OpenAI) -> None:
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"])
+ assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"])
@parametrize
def test_streaming_response_create(self, client: OpenAI) -> None:
@@ -60,7 +60,7 @@ def test_streaming_response_create(self, client: OpenAI) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
transcription = response.parse()
- assert_matches_type(Transcription, transcription, path=["response"])
+ assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -74,7 +74,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
file=b"raw file contents",
model="whisper-1",
)
- assert_matches_type(Transcription, transcription, path=["response"])
+ assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"])
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
@@ -87,7 +87,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) ->
temperature=0,
timestamp_granularities=["word", "segment"],
)
- assert_matches_type(Transcription, transcription, path=["response"])
+ assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"])
@parametrize
async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
@@ -99,7 +99,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
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"])
+ assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"])
@parametrize
async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
@@ -111,6 +111,6 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
transcription = await response.parse()
- assert_matches_type(Transcription, transcription, path=["response"])
+ assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"])
assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py
index b048a1af12..e12ab7e6c0 100644
--- a/tests/api_resources/audio/test_translations.py
+++ b/tests/api_resources/audio/test_translations.py
@@ -9,7 +9,7 @@
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
-from openai.types.audio import Translation
+from openai.types.audio import TranslationCreateResponse
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -23,7 +23,7 @@ def test_method_create(self, client: OpenAI) -> None:
file=b"raw file contents",
model="whisper-1",
)
- assert_matches_type(Translation, translation, path=["response"])
+ assert_matches_type(TranslationCreateResponse, translation, path=["response"])
@parametrize
def test_method_create_with_all_params(self, client: OpenAI) -> None:
@@ -34,7 +34,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None:
response_format="json",
temperature=0,
)
- assert_matches_type(Translation, translation, path=["response"])
+ assert_matches_type(TranslationCreateResponse, translation, path=["response"])
@parametrize
def test_raw_response_create(self, client: OpenAI) -> None:
@@ -46,7 +46,7 @@ def test_raw_response_create(self, client: OpenAI) -> None:
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"])
+ assert_matches_type(TranslationCreateResponse, translation, path=["response"])
@parametrize
def test_streaming_response_create(self, client: OpenAI) -> None:
@@ -58,7 +58,7 @@ def test_streaming_response_create(self, client: OpenAI) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
translation = response.parse()
- assert_matches_type(Translation, translation, path=["response"])
+ assert_matches_type(TranslationCreateResponse, translation, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -72,7 +72,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
file=b"raw file contents",
model="whisper-1",
)
- assert_matches_type(Translation, translation, path=["response"])
+ assert_matches_type(TranslationCreateResponse, translation, path=["response"])
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
@@ -83,7 +83,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) ->
response_format="json",
temperature=0,
)
- assert_matches_type(Translation, translation, path=["response"])
+ assert_matches_type(TranslationCreateResponse, translation, path=["response"])
@parametrize
async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
@@ -95,7 +95,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
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"])
+ assert_matches_type(TranslationCreateResponse, translation, path=["response"])
@parametrize
async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
@@ -107,6 +107,6 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
translation = await response.parse()
- assert_matches_type(Translation, translation, path=["response"])
+ assert_matches_type(TranslationCreateResponse, translation, path=["response"])
assert cast(Any, response.is_closed) is True
diff --git a/tests/lib/test_audio.py b/tests/lib/test_audio.py
new file mode 100644
index 0000000000..0f53b316ba
--- /dev/null
+++ b/tests/lib/test_audio.py
@@ -0,0 +1,83 @@
+from __future__ import annotations
+
+import sys
+import inspect
+import typing_extensions
+from typing import get_args
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import evaluate_forwardref
+from openai._utils import assert_signatures_in_sync
+from openai._compat import is_literal_type
+from openai._utils._typing import is_union_type
+from openai.types.audio_response_format import AudioResponseFormat
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+def test_translation_create_overloads_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
+ checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
+
+ fn = checking_client.audio.translations.create
+ overload_response_formats: set[str] = set()
+
+ for i, overload in enumerate(typing_extensions.get_overloads(fn)):
+ assert_signatures_in_sync(
+ fn,
+ overload,
+ exclude_params={"response_format"},
+ description=f" for overload {i}",
+ )
+
+ sig = inspect.signature(overload)
+ typ = evaluate_forwardref(
+ sig.parameters["response_format"].annotation,
+ globalns=sys.modules[fn.__module__].__dict__,
+ )
+ if is_union_type(typ):
+ for arg in get_args(typ):
+ if not is_literal_type(arg):
+ continue
+
+ overload_response_formats.update(get_args(arg))
+ elif is_literal_type(typ):
+ overload_response_formats.update(get_args(typ))
+
+ src_response_formats: set[str] = set(get_args(AudioResponseFormat))
+ diff = src_response_formats.difference(overload_response_formats)
+ assert len(diff) == 0, f"some response format options don't have overloads"
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+def test_transcription_create_overloads_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
+ checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
+
+ fn = checking_client.audio.transcriptions.create
+ overload_response_formats: set[str] = set()
+
+ for i, overload in enumerate(typing_extensions.get_overloads(fn)):
+ assert_signatures_in_sync(
+ fn,
+ overload,
+ exclude_params={"response_format"},
+ description=f" for overload {i}",
+ )
+
+ sig = inspect.signature(overload)
+ typ = evaluate_forwardref(
+ sig.parameters["response_format"].annotation,
+ globalns=sys.modules[fn.__module__].__dict__,
+ )
+ if is_union_type(typ):
+ for arg in get_args(typ):
+ if not is_literal_type(arg):
+ continue
+
+ overload_response_formats.update(get_args(arg))
+ elif is_literal_type(typ):
+ overload_response_formats.update(get_args(typ))
+
+ src_response_formats: set[str] = set(get_args(AudioResponseFormat))
+ diff = src_response_formats.difference(overload_response_formats)
+ assert len(diff) == 0, f"some response format options don't have overloads"
diff --git a/tests/utils.py b/tests/utils.py
index 8d5397f28e..16948a66f2 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -5,7 +5,7 @@
import inspect
import traceback
import contextlib
-from typing import Any, TypeVar, Iterator, cast
+from typing import Any, TypeVar, Iterator, ForwardRef, cast
from datetime import date, datetime
from typing_extensions import Literal, get_args, get_origin, assert_type
@@ -26,6 +26,10 @@
BaseModelT = TypeVar("BaseModelT", bound=BaseModel)
+def evaluate_forwardref(forwardref: ForwardRef, globalns: dict[str, Any]) -> type:
+ return eval(str(forwardref), globalns) # type: ignore[no-any-return]
+
+
def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool:
for name, field in get_model_fields(model).items():
field_value = getattr(value, name)