From 4e46e8ceb9468bd2512430d8c3448af94b17c865 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:04:45 +0000 Subject: [PATCH 1/3] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 59c2b6bf..f28fab45 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 946 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/telnyx%2Ftelnyx-902a5f08430969510f0127e6985bb631c0c928b80d14b6f75ec8b71bd7c397b5.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/telnyx%2Ftelnyx-46d2b18a142f548ea52a09d0be15f01dc026cbe1c8aeaa0576bbbf9516d1edfa.yml openapi_spec_hash: 515f3d122ed486daa3f9a90a252908c8 -config_hash: 6f6c7ba66f40d55677e28da77c03ea96 +config_hash: 5fe73f94aace4c5e5996ce2eb60ac097 From 7c4e7096921ae6e07ede51b783711bbdcd9c088a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 12:10:21 +0000 Subject: [PATCH 2/3] feat: Add text-to-speech WebSocket streaming OpenAPI spec --- .stats.yml | 6 +- api.md | 11 +- src/telnyx/resources/queues/queues.py | 14 +- src/telnyx/resources/text_to_speech.py | 284 +++++++++++------- src/telnyx/types/__init__.py | 4 +- src/telnyx/types/queue_create_response.py | 34 ++- .../{queue.py => queue_list_response.py} | 4 +- src/telnyx/types/queue_retrieve_response.py | 34 ++- src/telnyx/types/queue_update_response.py | 34 ++- .../text_to_speech_generate_speech_params.py | 26 -- .../types/text_to_speech_stream_params.py | 49 +++ tests/api_resources/test_queues.py | 18 +- tests/api_resources/test_text_to_speech.py | 174 +++++------ 13 files changed, 428 insertions(+), 264 deletions(-) rename src/telnyx/types/{queue.py => queue_list_response.py} (91%) delete mode 100644 src/telnyx/types/text_to_speech_generate_speech_params.py create mode 100644 src/telnyx/types/text_to_speech_stream_params.py diff --git a/.stats.yml b/.stats.yml index f28fab45..6eb74933 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 946 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/telnyx%2Ftelnyx-46d2b18a142f548ea52a09d0be15f01dc026cbe1c8aeaa0576bbbf9516d1edfa.yml -openapi_spec_hash: 515f3d122ed486daa3f9a90a252908c8 -config_hash: 5fe73f94aace4c5e5996ce2eb60ac097 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/telnyx%2Ftelnyx-619f2e4cc820442ae564e2345323abd65d5049b428c5780cf9f14c56002a2ba3.yml +openapi_spec_hash: 29de78582d8561745ff8f5f6c2474f1e +config_hash: 535b049bef73708a7530337b14fd0b84 diff --git a/api.md b/api.md index ffcc76d7..5902c352 100644 --- a/api.md +++ b/api.md @@ -3544,7 +3544,12 @@ Methods: Types: ```python -from telnyx.types import Queue, QueueCreateResponse, QueueRetrieveResponse, QueueUpdateResponse +from telnyx.types import ( + QueueCreateResponse, + QueueRetrieveResponse, + QueueUpdateResponse, + QueueListResponse, +) ``` Methods: @@ -3552,7 +3557,7 @@ Methods: - client.queues.create(\*\*params) -> QueueCreateResponse - client.queues.retrieve(queue_name) -> QueueRetrieveResponse - client.queues.update(queue_name, \*\*params) -> QueueUpdateResponse -- client.queues.list(\*\*params) -> SyncDefaultFlatPagination[Queue] +- client.queues.list(\*\*params) -> SyncDefaultFlatPagination[QueueListResponse] - client.queues.delete(queue_name) -> None ## Calls @@ -4464,8 +4469,8 @@ from telnyx.types import TextToSpeechListVoicesResponse Methods: -- client.text_to_speech.generate_speech(\*\*params) -> BinaryAPIResponse - client.text_to_speech.list_voices(\*\*params) -> TextToSpeechListVoicesResponse +- client.text_to_speech.stream(\*\*params) -> None # UsageReports diff --git a/src/telnyx/resources/queues/queues.py b/src/telnyx/resources/queues/queues.py index 648dd6e9..2a62a667 100644 --- a/src/telnyx/resources/queues/queues.py +++ b/src/telnyx/resources/queues/queues.py @@ -24,8 +24,8 @@ async_to_streamed_response_wrapper, ) from ...pagination import SyncDefaultFlatPagination, AsyncDefaultFlatPagination -from ...types.queue import Queue from ..._base_client import AsyncPaginator, make_request_options +from ...types.queue_list_response import QueueListResponse from ...types.queue_create_response import QueueCreateResponse from ...types.queue_update_response import QueueUpdateResponse from ...types.queue_retrieve_response import QueueRetrieveResponse @@ -182,7 +182,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncDefaultFlatPagination[Queue]: + ) -> SyncDefaultFlatPagination[QueueListResponse]: """ List all queues for the authenticated user. @@ -201,7 +201,7 @@ def list( """ return self._get_api_list( "/queues", - page=SyncDefaultFlatPagination[Queue], + page=SyncDefaultFlatPagination[QueueListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -215,7 +215,7 @@ def list( queue_list_params.QueueListParams, ), ), - model=Queue, + model=QueueListResponse, ) def delete( @@ -402,7 +402,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Queue, AsyncDefaultFlatPagination[Queue]]: + ) -> AsyncPaginator[QueueListResponse, AsyncDefaultFlatPagination[QueueListResponse]]: """ List all queues for the authenticated user. @@ -421,7 +421,7 @@ def list( """ return self._get_api_list( "/queues", - page=AsyncDefaultFlatPagination[Queue], + page=AsyncDefaultFlatPagination[QueueListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -435,7 +435,7 @@ def list( queue_list_params.QueueListParams, ), ), - model=Queue, + model=QueueListResponse, ) async def delete( diff --git a/src/telnyx/resources/text_to_speech.py b/src/telnyx/resources/text_to_speech.py index 7e37a6f5..4b9ff0e3 100644 --- a/src/telnyx/resources/text_to_speech.py +++ b/src/telnyx/resources/text_to_speech.py @@ -6,24 +6,16 @@ import httpx -from ..types import text_to_speech_list_voices_params, text_to_speech_generate_speech_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..types import text_to_speech_stream_params, text_to_speech_list_voices_params +from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( - BinaryAPIResponse, - AsyncBinaryAPIResponse, - StreamedBinaryAPIResponse, - AsyncStreamedBinaryAPIResponse, to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, - to_custom_raw_response_wrapper, async_to_streamed_response_wrapper, - to_custom_streamed_response_wrapper, - async_to_custom_raw_response_wrapper, - async_to_custom_streamed_response_wrapper, ) from .._base_client import make_request_options from ..types.text_to_speech_list_voices_response import TextToSpeechListVoicesResponse @@ -51,36 +43,25 @@ def with_streaming_response(self) -> TextToSpeechResourceWithStreamingResponse: """ return TextToSpeechResourceWithStreamingResponse(self) - def generate_speech( + def list_voices( self, *, - text: str, - voice: str, + elevenlabs_api_key_ref: str | Omit = omit, + provider: Literal["aws", "azure", "elevenlabs", "telnyx"] | Omit = omit, # 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, - ) -> BinaryAPIResponse: + ) -> TextToSpeechListVoicesResponse: """ - Converts the provided text to speech using the specified voice and returns audio - data + Returns a list of voices that can be used with the text to speech commands. Args: - text: The text to convert to speech - - voice: The voice ID in the format Provider.ModelId.VoiceId. - - Examples: - - - AWS.Polly.Joanna-Neural - - Azure.en-US-AvaMultilingualNeural - - ElevenLabs.eleven_multilingual_v2.Rachel - - Telnyx.KokoroTTS.af + elevenlabs_api_key_ref: Reference to your ElevenLabs API key stored in the Telnyx Portal - Use the `GET /text-to-speech/voices` endpoint to get a complete list of - available voices. + provider: Filter voices by provider extra_headers: Send extra headers @@ -90,41 +71,86 @@ def generate_speech( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = {"Accept": "audio/mpeg", **(extra_headers or {})} - return self._post( - "/text-to-speech/speech", - body=maybe_transform( - { - "text": text, - "voice": voice, - }, - text_to_speech_generate_speech_params.TextToSpeechGenerateSpeechParams, - ), + return self._get( + "/text-to-speech/voices", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "elevenlabs_api_key_ref": elevenlabs_api_key_ref, + "provider": provider, + }, + text_to_speech_list_voices_params.TextToSpeechListVoicesParams, + ), ), - cast_to=BinaryAPIResponse, + cast_to=TextToSpeechListVoicesResponse, ) - def list_voices( + def stream( self, *, - elevenlabs_api_key_ref: str | Omit = omit, - provider: Literal["aws", "azure", "elevenlabs", "telnyx"] | Omit = omit, + audio_format: Literal["pcm", "wav"] | Omit = omit, + disable_cache: bool | Omit = omit, + model_id: str | Omit = omit, + provider: Literal["aws", "telnyx", "azure", "elevenlabs", "minimax", "murfai", "rime", "resemble"] + | Omit = omit, + socket_id: str | Omit = omit, + voice: str | Omit = omit, + voice_id: str | Omit = omit, # 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, - ) -> TextToSpeechListVoicesResponse: + ) -> None: """ - Returns a list of voices that can be used with the text to speech commands. + Open a WebSocket connection to stream text and receive synthesized audio in real + time. Authentication is provided via the standard + `Authorization: Bearer ` header. Send JSON frames with text to + synthesize; receive JSON frames containing base64-encoded audio chunks. + + Supported providers: `aws`, `telnyx`, `azure`, `murfai`, `minimax`, `rime`, + `resemble`, `elevenlabs`. + + **Connection flow:** + + 1. Open WebSocket with query parameters specifying provider, voice, and model. + 2. Send an initial handshake message `{"text": " "}` (single space) with + optional `voice_settings` to initialize the session. + 3. Send text messages as `{"text": "Hello world"}`. + 4. Receive audio chunks as JSON frames with base64-encoded audio. + 5. A final frame with `isFinal: true` indicates the end of audio for the current + text. + + To interrupt and restart synthesis mid-stream, send `{"force": true}` — the + current worker is stopped and a new one is started. Args: - elevenlabs_api_key_ref: Reference to your ElevenLabs API key stored in the Telnyx Portal + audio_format: Audio output format override. Supported for Telnyx `Natural`/`NaturalHD` models + only. Accepted values: `pcm`, `wav`. - provider: Filter voices by provider + disable_cache: When `true`, bypass the audio cache and generate fresh audio. + + model_id: Model identifier for the chosen provider. Examples: `Natural`, `NaturalHD` + (Telnyx); `Polly.Generative` (AWS). + + provider: TTS provider. Defaults to `telnyx` if not specified. Ignored when `voice` is + provided. + + socket_id: Client-provided socket identifier for tracking. If not provided, one is + generated server-side. + + voice: Voice identifier in the format `provider.model_id.voice_id` or + `provider.voice_id` (e.g. `telnyx.NaturalHD.Telnyx_Alloy` or + `azure.en-US-AvaMultilingualNeural`). When provided, the `provider`, `model_id`, + and `voice_id` are extracted automatically. Takes precedence over individual + `provider`/`model_id`/`voice_id` parameters. + + voice_id: Voice identifier for the chosen provider. extra_headers: Send extra headers @@ -134,8 +160,9 @@ def list_voices( timeout: Override the client-level default timeout for this request, in seconds """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._get( - "/text-to-speech/voices", + "/text-to-speech/speech", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -143,13 +170,18 @@ def list_voices( timeout=timeout, query=maybe_transform( { - "elevenlabs_api_key_ref": elevenlabs_api_key_ref, + "audio_format": audio_format, + "disable_cache": disable_cache, + "model_id": model_id, "provider": provider, + "socket_id": socket_id, + "voice": voice, + "voice_id": voice_id, }, - text_to_speech_list_voices_params.TextToSpeechListVoicesParams, + text_to_speech_stream_params.TextToSpeechStreamParams, ), ), - cast_to=TextToSpeechListVoicesResponse, + cast_to=NoneType, ) @@ -173,36 +205,25 @@ def with_streaming_response(self) -> AsyncTextToSpeechResourceWithStreamingRespo """ return AsyncTextToSpeechResourceWithStreamingResponse(self) - async def generate_speech( + async def list_voices( self, *, - text: str, - voice: str, + elevenlabs_api_key_ref: str | Omit = omit, + provider: Literal["aws", "azure", "elevenlabs", "telnyx"] | Omit = omit, # 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, - ) -> AsyncBinaryAPIResponse: + ) -> TextToSpeechListVoicesResponse: """ - Converts the provided text to speech using the specified voice and returns audio - data + Returns a list of voices that can be used with the text to speech commands. Args: - text: The text to convert to speech - - voice: The voice ID in the format Provider.ModelId.VoiceId. - - Examples: - - - AWS.Polly.Joanna-Neural - - Azure.en-US-AvaMultilingualNeural - - ElevenLabs.eleven_multilingual_v2.Rachel - - Telnyx.KokoroTTS.af + elevenlabs_api_key_ref: Reference to your ElevenLabs API key stored in the Telnyx Portal - Use the `GET /text-to-speech/voices` endpoint to get a complete list of - available voices. + provider: Filter voices by provider extra_headers: Send extra headers @@ -212,41 +233,86 @@ async def generate_speech( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = {"Accept": "audio/mpeg", **(extra_headers or {})} - return await self._post( - "/text-to-speech/speech", - body=await async_maybe_transform( - { - "text": text, - "voice": voice, - }, - text_to_speech_generate_speech_params.TextToSpeechGenerateSpeechParams, - ), + return await self._get( + "/text-to-speech/voices", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "elevenlabs_api_key_ref": elevenlabs_api_key_ref, + "provider": provider, + }, + text_to_speech_list_voices_params.TextToSpeechListVoicesParams, + ), ), - cast_to=AsyncBinaryAPIResponse, + cast_to=TextToSpeechListVoicesResponse, ) - async def list_voices( + async def stream( self, *, - elevenlabs_api_key_ref: str | Omit = omit, - provider: Literal["aws", "azure", "elevenlabs", "telnyx"] | Omit = omit, + audio_format: Literal["pcm", "wav"] | Omit = omit, + disable_cache: bool | Omit = omit, + model_id: str | Omit = omit, + provider: Literal["aws", "telnyx", "azure", "elevenlabs", "minimax", "murfai", "rime", "resemble"] + | Omit = omit, + socket_id: str | Omit = omit, + voice: str | Omit = omit, + voice_id: str | Omit = omit, # 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, - ) -> TextToSpeechListVoicesResponse: + ) -> None: """ - Returns a list of voices that can be used with the text to speech commands. + Open a WebSocket connection to stream text and receive synthesized audio in real + time. Authentication is provided via the standard + `Authorization: Bearer ` header. Send JSON frames with text to + synthesize; receive JSON frames containing base64-encoded audio chunks. + + Supported providers: `aws`, `telnyx`, `azure`, `murfai`, `minimax`, `rime`, + `resemble`, `elevenlabs`. + + **Connection flow:** + + 1. Open WebSocket with query parameters specifying provider, voice, and model. + 2. Send an initial handshake message `{"text": " "}` (single space) with + optional `voice_settings` to initialize the session. + 3. Send text messages as `{"text": "Hello world"}`. + 4. Receive audio chunks as JSON frames with base64-encoded audio. + 5. A final frame with `isFinal: true` indicates the end of audio for the current + text. + + To interrupt and restart synthesis mid-stream, send `{"force": true}` — the + current worker is stopped and a new one is started. Args: - elevenlabs_api_key_ref: Reference to your ElevenLabs API key stored in the Telnyx Portal + audio_format: Audio output format override. Supported for Telnyx `Natural`/`NaturalHD` models + only. Accepted values: `pcm`, `wav`. - provider: Filter voices by provider + disable_cache: When `true`, bypass the audio cache and generate fresh audio. + + model_id: Model identifier for the chosen provider. Examples: `Natural`, `NaturalHD` + (Telnyx); `Polly.Generative` (AWS). + + provider: TTS provider. Defaults to `telnyx` if not specified. Ignored when `voice` is + provided. + + socket_id: Client-provided socket identifier for tracking. If not provided, one is + generated server-side. + + voice: Voice identifier in the format `provider.model_id.voice_id` or + `provider.voice_id` (e.g. `telnyx.NaturalHD.Telnyx_Alloy` or + `azure.en-US-AvaMultilingualNeural`). When provided, the `provider`, `model_id`, + and `voice_id` are extracted automatically. Takes precedence over individual + `provider`/`model_id`/`voice_id` parameters. + + voice_id: Voice identifier for the chosen provider. extra_headers: Send extra headers @@ -256,8 +322,9 @@ async def list_voices( timeout: Override the client-level default timeout for this request, in seconds """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._get( - "/text-to-speech/voices", + "/text-to-speech/speech", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -265,13 +332,18 @@ async def list_voices( timeout=timeout, query=await async_maybe_transform( { - "elevenlabs_api_key_ref": elevenlabs_api_key_ref, + "audio_format": audio_format, + "disable_cache": disable_cache, + "model_id": model_id, "provider": provider, + "socket_id": socket_id, + "voice": voice, + "voice_id": voice_id, }, - text_to_speech_list_voices_params.TextToSpeechListVoicesParams, + text_to_speech_stream_params.TextToSpeechStreamParams, ), ), - cast_to=TextToSpeechListVoicesResponse, + cast_to=NoneType, ) @@ -279,49 +351,45 @@ class TextToSpeechResourceWithRawResponse: def __init__(self, text_to_speech: TextToSpeechResource) -> None: self._text_to_speech = text_to_speech - self.generate_speech = to_custom_raw_response_wrapper( - text_to_speech.generate_speech, - BinaryAPIResponse, - ) self.list_voices = to_raw_response_wrapper( text_to_speech.list_voices, ) + self.stream = to_raw_response_wrapper( + text_to_speech.stream, + ) class AsyncTextToSpeechResourceWithRawResponse: def __init__(self, text_to_speech: AsyncTextToSpeechResource) -> None: self._text_to_speech = text_to_speech - self.generate_speech = async_to_custom_raw_response_wrapper( - text_to_speech.generate_speech, - AsyncBinaryAPIResponse, - ) self.list_voices = async_to_raw_response_wrapper( text_to_speech.list_voices, ) + self.stream = async_to_raw_response_wrapper( + text_to_speech.stream, + ) class TextToSpeechResourceWithStreamingResponse: def __init__(self, text_to_speech: TextToSpeechResource) -> None: self._text_to_speech = text_to_speech - self.generate_speech = to_custom_streamed_response_wrapper( - text_to_speech.generate_speech, - StreamedBinaryAPIResponse, - ) self.list_voices = to_streamed_response_wrapper( text_to_speech.list_voices, ) + self.stream = to_streamed_response_wrapper( + text_to_speech.stream, + ) class AsyncTextToSpeechResourceWithStreamingResponse: def __init__(self, text_to_speech: AsyncTextToSpeechResource) -> None: self._text_to_speech = text_to_speech - self.generate_speech = async_to_custom_streamed_response_wrapper( - text_to_speech.generate_speech, - AsyncStreamedBinaryAPIResponse, - ) self.list_voices = async_to_streamed_response_wrapper( text_to_speech.list_voices, ) + self.stream = async_to_streamed_response_wrapper( + text_to_speech.stream, + ) diff --git a/src/telnyx/types/__init__.py b/src/telnyx/types/__init__.py index 10f0b762..976846fe 100644 --- a/src/telnyx/types/__init__.py +++ b/src/telnyx/types/__init__.py @@ -8,7 +8,6 @@ from .fax import Fax as Fax from .fqdn import Fqdn as Fqdn from .room import Room as Room -from .queue import Queue as Queue from .record import Record as Record from .shared import ( APIError as APIError, @@ -171,6 +170,7 @@ from .outbound_fqdn_param import OutboundFqdnParam as OutboundFqdnParam from .portout_list_params import PortoutListParams as PortoutListParams from .queue_create_params import QueueCreateParams as QueueCreateParams +from .queue_list_response import QueueListResponse as QueueListResponse from .queue_update_params import QueueUpdateParams as QueueUpdateParams from .replaced_link_click import ReplacedLinkClick as ReplacedLinkClick from .verify_profile_data import VerifyProfileData as VerifyProfileData @@ -437,6 +437,7 @@ from .sim_card_group_update_params import SimCardGroupUpdateParams as SimCardGroupUpdateParams from .sim_card_order_create_params import SimCardOrderCreateParams as SimCardOrderCreateParams from .sub_number_order_list_params import SubNumberOrderListParams as SubNumberOrderListParams +from .text_to_speech_stream_params import TextToSpeechStreamParams as TextToSpeechStreamParams from .url_shortener_settings_param import URLShortenerSettingsParam as URLShortenerSettingsParam from .user_address_create_response import UserAddressCreateResponse as UserAddressCreateResponse from .verified_number_data_wrapper import VerifiedNumberDataWrapper as VerifiedNumberDataWrapper @@ -830,7 +831,6 @@ ) from .sim_card_get_activation_code_response import SimCardGetActivationCodeResponse as SimCardGetActivationCodeResponse from .sim_card_order_preview_preview_params import SimCardOrderPreviewPreviewParams as SimCardOrderPreviewPreviewParams -from .text_to_speech_generate_speech_params import TextToSpeechGenerateSpeechParams as TextToSpeechGenerateSpeechParams from .verification_trigger_flashcall_params import ( VerificationTriggerFlashcallParams as VerificationTriggerFlashcallParams, ) diff --git a/src/telnyx/types/queue_create_response.py b/src/telnyx/types/queue_create_response.py index 987b3918..15bb5301 100644 --- a/src/telnyx/types/queue_create_response.py +++ b/src/telnyx/types/queue_create_response.py @@ -1,12 +1,40 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from typing_extensions import Literal -from .queue import Queue from .._models import BaseModel -__all__ = ["QueueCreateResponse"] +__all__ = ["QueueCreateResponse", "Data"] + + +class Data(BaseModel): + id: str + """Uniquely identifies the queue""" + + average_wait_time_secs: int + """ + The average time that the calls currently in the queue have spent waiting, given + in seconds. + """ + + created_at: str + """ISO 8601 formatted date of when the queue was created""" + + current_size: int + """The number of calls currently in the queue""" + + max_size: int + """The maximum number of calls allowed in the queue""" + + name: str + """Name of the queue""" + + record_type: Literal["queue"] + + updated_at: str + """ISO 8601 formatted date of when the queue was last updated""" class QueueCreateResponse(BaseModel): - data: Optional[Queue] = None + data: Optional[Data] = None diff --git a/src/telnyx/types/queue.py b/src/telnyx/types/queue_list_response.py similarity index 91% rename from src/telnyx/types/queue.py rename to src/telnyx/types/queue_list_response.py index 24e4c6ce..888ee525 100644 --- a/src/telnyx/types/queue.py +++ b/src/telnyx/types/queue_list_response.py @@ -4,10 +4,10 @@ from .._models import BaseModel -__all__ = ["Queue"] +__all__ = ["QueueListResponse"] -class Queue(BaseModel): +class QueueListResponse(BaseModel): id: str """Uniquely identifies the queue""" diff --git a/src/telnyx/types/queue_retrieve_response.py b/src/telnyx/types/queue_retrieve_response.py index f51d426d..2136e305 100644 --- a/src/telnyx/types/queue_retrieve_response.py +++ b/src/telnyx/types/queue_retrieve_response.py @@ -1,12 +1,40 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from typing_extensions import Literal -from .queue import Queue from .._models import BaseModel -__all__ = ["QueueRetrieveResponse"] +__all__ = ["QueueRetrieveResponse", "Data"] + + +class Data(BaseModel): + id: str + """Uniquely identifies the queue""" + + average_wait_time_secs: int + """ + The average time that the calls currently in the queue have spent waiting, given + in seconds. + """ + + created_at: str + """ISO 8601 formatted date of when the queue was created""" + + current_size: int + """The number of calls currently in the queue""" + + max_size: int + """The maximum number of calls allowed in the queue""" + + name: str + """Name of the queue""" + + record_type: Literal["queue"] + + updated_at: str + """ISO 8601 formatted date of when the queue was last updated""" class QueueRetrieveResponse(BaseModel): - data: Optional[Queue] = None + data: Optional[Data] = None diff --git a/src/telnyx/types/queue_update_response.py b/src/telnyx/types/queue_update_response.py index dee8a551..ab97b4da 100644 --- a/src/telnyx/types/queue_update_response.py +++ b/src/telnyx/types/queue_update_response.py @@ -1,12 +1,40 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from typing_extensions import Literal -from .queue import Queue from .._models import BaseModel -__all__ = ["QueueUpdateResponse"] +__all__ = ["QueueUpdateResponse", "Data"] + + +class Data(BaseModel): + id: str + """Uniquely identifies the queue""" + + average_wait_time_secs: int + """ + The average time that the calls currently in the queue have spent waiting, given + in seconds. + """ + + created_at: str + """ISO 8601 formatted date of when the queue was created""" + + current_size: int + """The number of calls currently in the queue""" + + max_size: int + """The maximum number of calls allowed in the queue""" + + name: str + """Name of the queue""" + + record_type: Literal["queue"] + + updated_at: str + """ISO 8601 formatted date of when the queue was last updated""" class QueueUpdateResponse(BaseModel): - data: Optional[Queue] = None + data: Optional[Data] = None diff --git a/src/telnyx/types/text_to_speech_generate_speech_params.py b/src/telnyx/types/text_to_speech_generate_speech_params.py deleted file mode 100644 index 95b43e53..00000000 --- a/src/telnyx/types/text_to_speech_generate_speech_params.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["TextToSpeechGenerateSpeechParams"] - - -class TextToSpeechGenerateSpeechParams(TypedDict, total=False): - text: Required[str] - """The text to convert to speech""" - - voice: Required[str] - """The voice ID in the format Provider.ModelId.VoiceId. - - Examples: - - - AWS.Polly.Joanna-Neural - - Azure.en-US-AvaMultilingualNeural - - ElevenLabs.eleven_multilingual_v2.Rachel - - Telnyx.KokoroTTS.af - - Use the `GET /text-to-speech/voices` endpoint to get a complete list of - available voices. - """ diff --git a/src/telnyx/types/text_to_speech_stream_params.py b/src/telnyx/types/text_to_speech_stream_params.py new file mode 100644 index 00000000..9a4aac90 --- /dev/null +++ b/src/telnyx/types/text_to_speech_stream_params.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["TextToSpeechStreamParams"] + + +class TextToSpeechStreamParams(TypedDict, total=False): + audio_format: Literal["pcm", "wav"] + """Audio output format override. + + Supported for Telnyx `Natural`/`NaturalHD` models only. Accepted values: `pcm`, + `wav`. + """ + + disable_cache: bool + """When `true`, bypass the audio cache and generate fresh audio.""" + + model_id: str + """Model identifier for the chosen provider. + + Examples: `Natural`, `NaturalHD` (Telnyx); `Polly.Generative` (AWS). + """ + + provider: Literal["aws", "telnyx", "azure", "elevenlabs", "minimax", "murfai", "rime", "resemble"] + """TTS provider. + + Defaults to `telnyx` if not specified. Ignored when `voice` is provided. + """ + + socket_id: str + """Client-provided socket identifier for tracking. + + If not provided, one is generated server-side. + """ + + voice: str + """ + Voice identifier in the format `provider.model_id.voice_id` or + `provider.voice_id` (e.g. `telnyx.NaturalHD.Telnyx_Alloy` or + `azure.en-US-AvaMultilingualNeural`). When provided, the `provider`, `model_id`, + and `voice_id` are extracted automatically. Takes precedence over individual + `provider`/`model_id`/`voice_id` parameters. + """ + + voice_id: str + """Voice identifier for the chosen provider.""" diff --git a/tests/api_resources/test_queues.py b/tests/api_resources/test_queues.py index ef672dcf..8caf76ab 100644 --- a/tests/api_resources/test_queues.py +++ b/tests/api_resources/test_queues.py @@ -10,7 +10,7 @@ from telnyx import Telnyx, AsyncTelnyx from tests.utils import assert_matches_type from telnyx.types import ( - Queue, + QueueListResponse, QueueCreateResponse, QueueUpdateResponse, QueueRetrieveResponse, @@ -158,7 +158,7 @@ def test_path_params_update(self, client: Telnyx) -> None: @parametrize def test_method_list(self, client: Telnyx) -> None: queue = client.queues.list() - assert_matches_type(SyncDefaultFlatPagination[Queue], queue, path=["response"]) + assert_matches_type(SyncDefaultFlatPagination[QueueListResponse], queue, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -167,7 +167,7 @@ def test_method_list_with_all_params(self, client: Telnyx) -> None: page_number=1, page_size=1, ) - assert_matches_type(SyncDefaultFlatPagination[Queue], queue, path=["response"]) + assert_matches_type(SyncDefaultFlatPagination[QueueListResponse], queue, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -177,7 +177,7 @@ def test_raw_response_list(self, client: Telnyx) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" queue = response.parse() - assert_matches_type(SyncDefaultFlatPagination[Queue], queue, path=["response"]) + assert_matches_type(SyncDefaultFlatPagination[QueueListResponse], queue, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -187,7 +187,7 @@ def test_streaming_response_list(self, client: Telnyx) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" queue = response.parse() - assert_matches_type(SyncDefaultFlatPagination[Queue], queue, path=["response"]) + assert_matches_type(SyncDefaultFlatPagination[QueueListResponse], queue, path=["response"]) assert cast(Any, response.is_closed) is True @@ -374,7 +374,7 @@ async def test_path_params_update(self, async_client: AsyncTelnyx) -> None: @parametrize async def test_method_list(self, async_client: AsyncTelnyx) -> None: queue = await async_client.queues.list() - assert_matches_type(AsyncDefaultFlatPagination[Queue], queue, path=["response"]) + assert_matches_type(AsyncDefaultFlatPagination[QueueListResponse], queue, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -383,7 +383,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncTelnyx) -> N page_number=1, page_size=1, ) - assert_matches_type(AsyncDefaultFlatPagination[Queue], queue, path=["response"]) + assert_matches_type(AsyncDefaultFlatPagination[QueueListResponse], queue, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -393,7 +393,7 @@ async def test_raw_response_list(self, async_client: AsyncTelnyx) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" queue = await response.parse() - assert_matches_type(AsyncDefaultFlatPagination[Queue], queue, path=["response"]) + assert_matches_type(AsyncDefaultFlatPagination[QueueListResponse], queue, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -403,7 +403,7 @@ async def test_streaming_response_list(self, async_client: AsyncTelnyx) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" queue = await response.parse() - assert_matches_type(AsyncDefaultFlatPagination[Queue], queue, path=["response"]) + assert_matches_type(AsyncDefaultFlatPagination[QueueListResponse], queue, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_text_to_speech.py b/tests/api_resources/test_text_to_speech.py index 2db1196b..19825e23 100644 --- a/tests/api_resources/test_text_to_speech.py +++ b/tests/api_resources/test_text_to_speech.py @@ -5,21 +5,11 @@ import os from typing import Any, cast -import httpx import pytest -from respx import MockRouter from telnyx import Telnyx, AsyncTelnyx from tests.utils import assert_matches_type -from telnyx.types import ( - TextToSpeechListVoicesResponse, -) -from telnyx._response import ( - BinaryAPIResponse, - AsyncBinaryAPIResponse, - StreamedBinaryAPIResponse, - AsyncStreamedBinaryAPIResponse, -) +from telnyx.types import TextToSpeechListVoicesResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -27,51 +17,6 @@ class TestTextToSpeech: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @parametrize - @pytest.mark.respx(base_url=base_url) - def test_method_generate_speech(self, client: Telnyx, respx_mock: MockRouter) -> None: - respx_mock.post("/text-to-speech/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - text_to_speech = client.text_to_speech.generate_speech( - text="text", - voice="voice", - ) - assert text_to_speech.is_closed - assert text_to_speech.json() == {"foo": "bar"} - assert cast(Any, text_to_speech.is_closed) is True - assert isinstance(text_to_speech, BinaryAPIResponse) - - @parametrize - @pytest.mark.respx(base_url=base_url) - def test_raw_response_generate_speech(self, client: Telnyx, respx_mock: MockRouter) -> None: - respx_mock.post("/text-to-speech/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - - text_to_speech = client.text_to_speech.with_raw_response.generate_speech( - text="text", - voice="voice", - ) - - assert text_to_speech.is_closed is True - assert text_to_speech.http_request.headers.get("X-Stainless-Lang") == "python" - assert text_to_speech.json() == {"foo": "bar"} - assert isinstance(text_to_speech, BinaryAPIResponse) - - @parametrize - @pytest.mark.respx(base_url=base_url) - def test_streaming_response_generate_speech(self, client: Telnyx, respx_mock: MockRouter) -> None: - respx_mock.post("/text-to-speech/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - with client.text_to_speech.with_streaming_response.generate_speech( - text="text", - voice="voice", - ) as text_to_speech: - assert not text_to_speech.is_closed - assert text_to_speech.http_request.headers.get("X-Stainless-Lang") == "python" - - assert text_to_speech.json() == {"foo": "bar"} - assert cast(Any, text_to_speech.is_closed) is True - assert isinstance(text_to_speech, StreamedBinaryAPIResponse) - - assert cast(Any, text_to_speech.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_list_voices(self, client: Telnyx) -> None: @@ -109,56 +54,53 @@ def test_streaming_response_list_voices(self, client: Telnyx) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_stream(self, client: Telnyx) -> None: + text_to_speech = client.text_to_speech.stream() + assert text_to_speech is None -class TestAsyncTextToSpeech: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - @pytest.mark.respx(base_url=base_url) - async def test_method_generate_speech(self, async_client: AsyncTelnyx, respx_mock: MockRouter) -> None: - respx_mock.post("/text-to-speech/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - text_to_speech = await async_client.text_to_speech.generate_speech( - text="text", + def test_method_stream_with_all_params(self, client: Telnyx) -> None: + text_to_speech = client.text_to_speech.stream( + audio_format="pcm", + disable_cache=True, + model_id="model_id", + provider="aws", + socket_id="socket_id", voice="voice", + voice_id="voice_id", ) - assert text_to_speech.is_closed - assert await text_to_speech.json() == {"foo": "bar"} - assert cast(Any, text_to_speech.is_closed) is True - assert isinstance(text_to_speech, AsyncBinaryAPIResponse) + assert text_to_speech is None + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - @pytest.mark.respx(base_url=base_url) - async def test_raw_response_generate_speech(self, async_client: AsyncTelnyx, respx_mock: MockRouter) -> None: - respx_mock.post("/text-to-speech/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + def test_raw_response_stream(self, client: Telnyx) -> None: + response = client.text_to_speech.with_raw_response.stream() - text_to_speech = await async_client.text_to_speech.with_raw_response.generate_speech( - text="text", - voice="voice", - ) - - assert text_to_speech.is_closed is True - assert text_to_speech.http_request.headers.get("X-Stainless-Lang") == "python" - assert await text_to_speech.json() == {"foo": "bar"} - assert isinstance(text_to_speech, AsyncBinaryAPIResponse) + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + text_to_speech = response.parse() + assert text_to_speech is None + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - @pytest.mark.respx(base_url=base_url) - async def test_streaming_response_generate_speech(self, async_client: AsyncTelnyx, respx_mock: MockRouter) -> None: - respx_mock.post("/text-to-speech/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - async with async_client.text_to_speech.with_streaming_response.generate_speech( - text="text", - voice="voice", - ) as text_to_speech: - assert not text_to_speech.is_closed - assert text_to_speech.http_request.headers.get("X-Stainless-Lang") == "python" + def test_streaming_response_stream(self, client: Telnyx) -> None: + with client.text_to_speech.with_streaming_response.stream() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + text_to_speech = response.parse() + assert text_to_speech is None + + assert cast(Any, response.is_closed) is True - assert await text_to_speech.json() == {"foo": "bar"} - assert cast(Any, text_to_speech.is_closed) is True - assert isinstance(text_to_speech, AsyncStreamedBinaryAPIResponse) - assert cast(Any, text_to_speech.is_closed) is True +class TestAsyncTextToSpeech: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -196,3 +138,45 @@ async def test_streaming_response_list_voices(self, async_client: AsyncTelnyx) - assert_matches_type(TextToSpeechListVoicesResponse, text_to_speech, path=["response"]) assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_stream(self, async_client: AsyncTelnyx) -> None: + text_to_speech = await async_client.text_to_speech.stream() + assert text_to_speech is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_stream_with_all_params(self, async_client: AsyncTelnyx) -> None: + text_to_speech = await async_client.text_to_speech.stream( + audio_format="pcm", + disable_cache=True, + model_id="model_id", + provider="aws", + socket_id="socket_id", + voice="voice", + voice_id="voice_id", + ) + assert text_to_speech is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_stream(self, async_client: AsyncTelnyx) -> None: + response = await async_client.text_to_speech.with_raw_response.stream() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + text_to_speech = await response.parse() + assert text_to_speech is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_stream(self, async_client: AsyncTelnyx) -> None: + async with async_client.text_to_speech.with_streaming_response.stream() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + text_to_speech = await response.parse() + assert text_to_speech is None + + assert cast(Any, response.is_closed) is True From a86a1d8b6f7976a50cf23b2d527581d435bd50a2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 12:10:46 +0000 Subject: [PATCH 3/3] release: 4.50.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/telnyx/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d351f029..2e339e66 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "4.49.0" + ".": "4.50.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a5703c1b..eff1182d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 4.50.0 (2026-02-26) + +Full Changelog: [v4.49.0...v4.50.0](https://github.com/team-telnyx/telnyx-python/compare/v4.49.0...v4.50.0) + +### Features + +* Add text-to-speech WebSocket streaming OpenAPI spec ([7c4e709](https://github.com/team-telnyx/telnyx-python/commit/7c4e7096921ae6e07ede51b783711bbdcd9c088a)) + ## 4.49.0 (2026-02-25) Full Changelog: [v4.48.0...v4.49.0](https://github.com/team-telnyx/telnyx-python/compare/v4.48.0...v4.49.0) diff --git a/pyproject.toml b/pyproject.toml index d9296359..06480867 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "telnyx" -version = "4.49.0" +version = "4.50.0" description = "Telnyx API SDK for global Voice, SMS, MMS, WhatsApp, Fax, Wireless IoT, SIP Trunking, and Call Control." dynamic = ["readme"] license = "MIT" diff --git a/src/telnyx/_version.py b/src/telnyx/_version.py index 32d6f397..576133ab 100644 --- a/src/telnyx/_version.py +++ b/src/telnyx/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "telnyx" -__version__ = "4.49.0" # x-release-please-version +__version__ = "4.50.0" # x-release-please-version