diff --git a/.fern/metadata.json b/.fern/metadata.json
index f810fd87..7799ad6e 100644
--- a/.fern/metadata.json
+++ b/.fern/metadata.json
@@ -16,8 +16,8 @@
"skip_validation": true
}
},
- "originGitCommit": "8dd6f48a06eee8c3985894f68ba3b554e5564d21",
+ "originGitCommit": "335af96251466afae7a9f71badb65c630a48626e",
"originGitCommitIsDirty": true,
"invokedBy": "manual",
- "sdkVersion": "7.3.2"
+ "sdkVersion": "7.4.1"
}
\ No newline at end of file
diff --git a/.fernignore b/.fernignore
index 3c065285..d868646d 100644
--- a/.fernignore
+++ b/.fernignore
@@ -26,6 +26,7 @@ src/deepgram/agent/v1/socket_client.py
src/deepgram/listen/v1/socket_client.py
src/deepgram/listen/v2/socket_client.py
src/deepgram/speak/v1/socket_client.py
+src/deepgram/speak/v2/socket_client.py
# Backward-compat patch: AgentV1SettingsAgentContext schema restructure as of
# 2026-05-05. The new schema nests messages under .context.messages; this file
@@ -125,13 +126,16 @@ src/deepgram/core/query_encoder.py
# Hand-written custom tests
tests/custom/test_agent_history.py
+tests/custom/test_agent_update_listen.py
tests/custom/test_compat_aliases.py
+tests/custom/test_eot_thresholds_feature.py
tests/custom/test_language_hint_compat.py
tests/custom/test_language_hints_feature.py
tests/custom/test_listen_v2_regen_constraints.py
tests/custom/test_query_encoder.py
tests/custom/test_secure_logging.py
tests/custom/test_socket_client_shims.py
+tests/custom/test_speak_v2_socket.py
tests/custom/test_text_builder.py
tests/custom/test_transport.py
tests/typecheck/compat_aliases.py
diff --git a/AGENTS.md b/AGENTS.md
index fd3febac..8e7f3afc 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -53,6 +53,7 @@ How to identify:
Current temporarily frozen files:
- `src/deepgram/speak/v1/socket_client.py` — optional message param defaults, broad exception catch
+- `src/deepgram/speak/v2/socket_client.py` — same (optional `send_flush`/`send_close` defaults, broad exception catch); new websocket TTS client added in the 2026-07-08 regen
- `src/deepgram/listen/v1/socket_client.py` — same
- `src/deepgram/listen/v2/socket_client.py` — same + `send_configure` typing.Any/raw shim, response Union uses typing.Any instead of `ListenV2ConfigureSuccess`
- `src/deepgram/agent/v1/socket_client.py` — same + `_sanitize_numeric_types`
diff --git a/examples/25-text-to-speech-streaming-v2.py b/examples/25-text-to-speech-streaming-v2.py
new file mode 100644
index 00000000..cd5fcd7f
--- /dev/null
+++ b/examples/25-text-to-speech-streaming-v2.py
@@ -0,0 +1,66 @@
+"""
+Example: Text-to-Speech Streaming with the Speak V2 (Flux) WebSocket
+
+This example shows how to stream text-to-speech conversion using the Speak V2
+WebSocket. Speak V2 uses Flux voices (model strings of the form
+`flux-{voice}-{language}`, e.g. `flux-alexis-en`); use Speak V1 for Aura voices.
+"""
+
+from typing import Union
+
+from dotenv import load_dotenv
+
+load_dotenv()
+
+from deepgram import DeepgramClient
+from deepgram.core.events import EventType
+from deepgram.speak.v2.types import SpeakV2Speak
+
+SpeakV2SocketClientResponse = Union[str, bytes]
+
+client = DeepgramClient()
+
+try:
+ with client.speak.v2.connect(model="flux-alexis-en", encoding="linear16", sample_rate="24000") as connection:
+
+ def on_message(message: SpeakV2SocketClientResponse) -> None:
+ if isinstance(message, bytes):
+ print("Received audio data")
+ # In production, you would write this audio data to a file or play it
+ # with open("output.raw", "ab") as audio_file:
+ # audio_file.write(message)
+ else:
+ msg_type = getattr(message, "type", "Unknown")
+ print(f"Received {msg_type} event")
+
+ connection.on(EventType.OPEN, lambda _: print("Connection opened"))
+ connection.on(EventType.MESSAGE, on_message)
+ connection.on(EventType.CLOSE, lambda _: print("Connection closed"))
+ connection.on(EventType.ERROR, lambda error: print(f"Error: {error}"))
+
+ # For sync version: Send messages before starting to listen
+ # Note: start_listening() blocks, so send all messages first
+ # For better control with bidirectional communication, use the async version
+ connection.send_speak(SpeakV2Speak(text="Hello, this is a text to speech example."))
+
+ # Flush to ensure all text is processed
+ connection.send_flush()
+
+ # Close the connection when done
+ connection.send_close()
+
+ # Start listening - this blocks until the connection closes
+ # All messages should be sent before calling this in sync mode
+ connection.start_listening()
+
+ # For async version:
+ # from deepgram import AsyncDeepgramClient
+ # async with client.speak.v2.connect(...) as connection:
+ # listen_task = asyncio.create_task(connection.start_listening())
+ # await connection.send_speak(SpeakV2Speak(text="..."))
+ # await connection.send_flush()
+ # await connection.send_close()
+ # await listen_task
+
+except Exception as e:
+ print(f"Error: {e}")
diff --git a/examples/README.md b/examples/README.md
index 61cf030c..819e723f 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -25,6 +25,7 @@ This directory contains comprehensive examples demonstrating how to use the Deep
- **22-text-builder-demo.py** - TextBuilder demo (no API key required)
- **23-text-builder-helper.py** - TextBuilder with REST API generation
- **24-text-builder-streaming.py** - TextBuilder with streaming TTS (WebSocket)
+- **25-text-to-speech-streaming-v2.py** - Streaming TTS via the Speak V2 (Flux) WebSocket
### 30-39: Voice Agent
diff --git a/poetry.lock b/poetry.lock
index 8f46bcc6..534ebf4f 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2,15 +2,15 @@
[[package]]
name = "aiohappyeyeballs"
-version = "2.6.2"
+version = "2.7.1"
description = "Happy Eyeballs for asyncio"
optional = true
python-versions = ">=3.10"
groups = ["main"]
markers = "extra == \"aiohttp\""
files = [
- {file = "aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4"},
- {file = "aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64"},
+ {file = "aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472"},
+ {file = "aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d"},
]
[[package]]
@@ -188,14 +188,14 @@ files = [
[[package]]
name = "anyio"
-version = "4.14.0"
+version = "4.14.1"
description = "High-level concurrency and networking framework on top of asyncio or Trio"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9"},
- {file = "anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89"},
+ {file = "anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72"},
+ {file = "anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e"},
]
[package.dependencies]
@@ -259,141 +259,105 @@ files = [
[[package]]
name = "charset-normalizer"
-version = "3.4.7"
+version = "3.4.9"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
- {file = "charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943"},
- {file = "charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00"},
- {file = "charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6"},
- {file = "charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110"},
- {file = "charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f"},
- {file = "charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c"},
- {file = "charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-win32.whl", hash = "sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207"},
- {file = "charset_normalizer-3.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-win32.whl", hash = "sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444"},
- {file = "charset_normalizer-3.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c"},
- {file = "charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d"},
- {file = "charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd6280cf040f233bd7d3407b743b4b4c74f70e8e1c4199cb112a62c941c0772a"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa99adc8f081b475a12843953db36831eaf83ec33eb46a90629ca6a5de45a616"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c1225416b463483160e4af85d5fc3a9690ccb53fd4b1865a6437825f5ede3209"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:16d10d789dd9bcca1173c95af82c58433122564b7bc39385124be735a35cbe99"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bb41182d93ea91f60b4bc8fbf4c820c69ef8a12ab2d917f3f1834f1acad07e8"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:bcf74c1df76758a395bf0af608c04c82257523f55c9868b334f06270d0f2112b"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b5314963fce9b0b12743891de876e724997864ee22aa496f903f426c7e2fa5b2"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e9701d0049d92c16703a42771b98d560b95248949f23f8cf7b4eddd201814fb9"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:65a7ff3f705e57d392f7261b6d0550fe137c3019477431f1c355e0db0a7d3e15"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79580094b00d1789d1f93ea55bc43cb2f611910c72235b7657f3482ddcc1b22d"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-win32.whl", hash = "sha256:432786d3561e69aeeae6c7e8648964ce0ad05736120135601f87ac26b9c83381"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:8c041122946b7ba21bb32c45b1aa57b1be35527690aeb3c5c234521085632eee"},
+ {file = "charset_normalizer-3.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:375b83ed0aecfce76c16d198fbc21f3b11b337d68662bea0a995046682a11419"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e94703ec9684807f20cfb5eed95c70f67f2a8f21ad620146d7b5a13677b93e5"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a441ea71902098ffe78c5abe6c494f44160b4af614ed16c3d9a3b1d17fd8ee2"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:304b13570067b2547562e308af560b3963857b1fa90bd6afd978130130fe2d6a"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4773092f8019072343a7447203308b176e10199920eb02d6195e81bbb3274c29"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:04ce310cb89c15df659582aee80a0603788732a5e017d5bd5c81158106ce249c"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:c0323c9daef75ef2e5083624b4585018a0c9d5e3b40f607eed81a311270b934b"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:871ff67ea1aad4dfd91736464934d56b32dac49f9fbe16cddba36198a7b3a0db"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67830fc78e67501f47bb950471b2dcb9b35b140084429318e862895a8e89c993"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3d92613ec25e43b05f042302531ec0f00b8445190e43325880cbd6ab7c2581da"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:280081916dc341820640489a66e4696049401ef1cf6dd672f672e70ad915aca3"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-win32.whl", hash = "sha256:ac351b3b8014eead140e77e9717e2992c6bbe30b63bc3422422eb84865412e3d"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:6366a16e1a25018694d6a5d784d09b046edc9eac40ea2b54065c3052672516a1"},
+ {file = "charset_normalizer-3.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:1d22856ffbe153a602df38e4a5464f0b748a54002e0d69ac6d2ad0a197cc99ec"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45b0cc4e3556cd875e09102988d1ab8356c998b596c9fced84547c8138b487a0"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b2aff1c7b3884512b9512c3eaadd9bab39fb45042ffaaa1dd08ff2b9f8109d9"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9104ed0bd76a429d46f9ec0dbc9b08ad1d2dcdf2b00a5a0daa1c145329b35b44"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b86a2b16095d250c6f58b3d9b2eee6f4147754344f3dab0922f7c9bf7d226c9"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e226f6218febc71f6c1fc2fafb91c226f75bdc1d8fb12d66823716e891608fd"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:90c44bc373b7687f6948b693cceaea1348ae0975d7474746559494468e3c1d84"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cdef90ae47919cae358d8ab15797a800ed41da7aba5d72419fb510729e2ed4b"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:60f44ade2cf573dad7a277e6f8ca9a51a21dda572b13bd7d8539bb3cd5dbedde"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a1786910334ed46ab1dd73222f2cd1e05c2c3bb39f6dddb4f8b36fc382058a39"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03d07803992c6c7bbc976327f34b18b6160327fc81cb82c9d504720ac0be3b62"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-win32.whl", hash = "sha256:78841cccf1af7b40f6f716338d50c0902dbe88d9f800b3c973b7a9a0a693a642"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:4b3dac63058cc36820b0dd072f89898604e2d39686fe05321729d00d8ac185a0"},
+ {file = "charset_normalizer-3.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:78fa18e436a1a0e58dbd7e02fc4473f3f32cceb12df9dfca542d075961c307d2"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115"},
+ {file = "charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-win32.whl", hash = "sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-win32.whl", hash = "sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177"},
+ {file = "charset_normalizer-3.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:253a4a220747e8b5faf57ec320c4f5efb0cef05f647420bf267143ec15dba10a"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68ce9f4d6b26d5ccbf7fd4459bf75f74a0a146677ebba80597df60cbdb20e6f4"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:58150c9f9b9a552505912d182ccdf26f6396fb6094816ceebcbb20eecabaed94"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df7276909358e5635ae203673ab7e509ddd224225a8d6b0790bf13eb2bde1cc5"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c09a49d6cde137258beb3d551994a2927fd35ad5cf96aed573f61bbd67c5f84"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:231ddcbb35e2ff8973e1365db41fe0572662893b99a05deb183b68ad4c0c8bd4"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:920079c3f7456fa213e0829ed2073aaa727fd39d889ead5b4f35d0de5460d04f"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0fa1aec2d32bcc03c8fa0f6f1712caad1adc38509f31142112e5c9daf5b9c833"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ad41ba96094304aa090f5a30cb6e4fb3b3f1c264c523394b4c39bbacc4dc92ba"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:43b9e366a31fdd1c87d0eb08f579b4a82b723ea54338f040d6b4e518a026ea29"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-win32.whl", hash = "sha256:93d59d504b230e83c7a843251681959a0b6a9cd76f6e146ce1b8a80eb8739af9"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-win_amd64.whl", hash = "sha256:ddf4af30b417d9fe16481e9b81c27ab2a7cde1ff7ba3e85653b02db7d145dc7b"},
+ {file = "charset_normalizer-3.4.9-cp39-cp39-win_arm64.whl", hash = "sha256:476743fe6dfe14a2da12e3ac79125dc84a3b2cf8094369a47a1529b0cd8549fe"},
+ {file = "charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5"},
+ {file = "charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b"},
]
[[package]]
@@ -1245,14 +1209,14 @@ windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pytest"
-version = "9.1.0"
+version = "9.1.1"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "pytest-9.1.0-py3-none-any.whl", hash = "sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32"},
- {file = "pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c"},
+ {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"},
+ {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"},
]
[package.dependencies]
@@ -1473,14 +1437,14 @@ urllib3 = ">=2"
[[package]]
name = "typing-extensions"
-version = "4.15.0"
+version = "4.16.0"
description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
- {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
- {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
+ {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"},
+ {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"},
]
[[package]]
diff --git a/pyproject.toml b/pyproject.toml
index 114b57e3..cce103b8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ dynamic = ["version"]
[tool.poetry]
name = "deepgram-sdk"
-version = "7.4.0"
+version = "7.4.1"
description = ""
readme = "README.md"
authors = []
diff --git a/reference.md b/reference.md
index 724b3388..2123ca1c 100644
--- a/reference.md
+++ b/reference.md
@@ -5757,6 +5757,283 @@ asyncio.run(main())
+## Speak V2 Connect
+
+client.speak.v2.connect(...)
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Convert text into natural-sounding speech using Deepgram's Flux TTS WebSocket
+
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from typing import Union
+
+from deepgram import DeepgramClient
+from deepgram.core.events import EventType
+from deepgram.speak.v2.types import (
+ SpeakV2Speak,
+ SpeakV2Connected,
+ SpeakV2SpeechStarted,
+ SpeakV2SpeechMetadata,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Warning,
+ SpeakV2Error,
+)
+
+SpeakV2Response = Union[
+ bytes,
+ SpeakV2Connected,
+ SpeakV2SpeechStarted,
+ SpeakV2SpeechMetadata,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Warning,
+ SpeakV2Error,
+]
+
+client = DeepgramClient(
+ api_key="YOUR_API_KEY",
+)
+
+with client.speak.v2.connect(
+ model="flux-alexis-en",
+ encoding="linear16",
+ sample_rate="24000"
+) as connection:
+ def on_message(message: SpeakV2Response) -> None:
+ if isinstance(message, bytes):
+ print("Received audio event")
+ else:
+ msg_type = getattr(message, "type", "Unknown")
+ print(f"Received {msg_type} event")
+
+ connection.on(EventType.OPEN, lambda _: print("Connection opened"))
+ connection.on(EventType.MESSAGE, on_message)
+ connection.on(EventType.CLOSE, lambda _: print("Connection closed"))
+ connection.on(EventType.ERROR, lambda error: print(f"Caught: {error}"))
+
+ # Start listening
+ connection.start_listening()
+
+ # Send text to be converted to speech
+ connection.send_speak(SpeakV2Speak(text="Hello, world!"))
+
+ # Send control messages
+ connection.send_flush()
+ connection.send_close()
+
+```
+
+
+
+
+
+
+#### 🔌 Async Usage
+
+
+-
+
+
+-
+
+```python
+import asyncio
+from typing import Union
+
+from deepgram import AsyncDeepgramClient
+from deepgram.core.events import EventType
+from deepgram.speak.v2.types import (
+ SpeakV2Speak,
+ SpeakV2Connected,
+ SpeakV2SpeechStarted,
+ SpeakV2SpeechMetadata,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Warning,
+ SpeakV2Error,
+)
+
+SpeakV2Response = Union[
+ bytes,
+ SpeakV2Connected,
+ SpeakV2SpeechStarted,
+ SpeakV2SpeechMetadata,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Warning,
+ SpeakV2Error,
+]
+
+client = AsyncDeepgramClient(
+ api_key="YOUR_API_KEY",
+)
+
+async def main():
+ async with client.speak.v2.connect(
+ model="flux-alexis-en",
+ encoding="linear16",
+ sample_rate="24000"
+ ) as connection:
+ def on_message(message: SpeakV2Response) -> None:
+ if isinstance(message, bytes):
+ print("Received audio event")
+ else:
+ msg_type = getattr(message, "type", "Unknown")
+ print(f"Received {msg_type} event")
+
+ connection.on(EventType.OPEN, lambda _: print("Connection opened"))
+ connection.on(EventType.MESSAGE, on_message)
+ connection.on(EventType.CLOSE, lambda _: print("Connection closed"))
+ connection.on(EventType.ERROR, lambda error: print(f"Caught: {error}"))
+
+ # Start listening
+ await connection.start_listening()
+
+ # Send text to be converted to speech
+ await connection.send_speak(SpeakV2Speak(text="Hello, world!"))
+
+ # Send control messages
+ await connection.send_flush()
+ await connection.send_close()
+
+asyncio.run(main())
+
+```
+
+
+
+
+
+
+#### 📤 Send Methods
+
+
+-
+
+
+-
+
+**`send_speak(message: SpeakV2Speak)`** — Send text to be converted to speech
+
+- `connection.send_speak(SpeakV2Speak(text="Hello, world!"))`
+
+
+
+
+
+-
+
+**`send_flush()`** — Process all queued text immediately
+
+- `connection.send_flush()`
+
+
+
+
+
+-
+
+**`send_close()`** — Close the connection
+
+- `connection.send_close()`
+
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**model:** `SpeakV2Model` — The Flux TTS model used to synthesize speech. Required on every connection. Model strings follow the format `flux-{voice}-{language}` (e.g. `flux-alexis-en`). Aura model strings are rejected on `/v2/speak`; use Speak V1 for Aura voices.
+
+
+
+
+
+-
+
+**encoding:** `typing.Optional[SpeakV2Encoding]` — Specify the expected encoding of your output audio (`linear16`, `mulaw`, `alaw`)
+
+
+
+
+
+-
+
+**sample_rate:** `typing.Optional[SpeakV2SampleRate]` — Sample rate for the output audio (`8000`, `16000`, `24000`, `32000`, `44100`, `48000`)
+
+
+
+
+
+-
+
+**mip_opt_out:** `typing.Optional[SpeakV2MipOptOut]` — Opts out requests from the Deepgram Model Improvement Program
+
+
+
+
+
+-
+
+**tag:** `typing.Optional[SpeakV2Tag]` — Label your requests for the purpose of identification during usage reporting. Repeatable.
+
+
+
+
+
+-
+
+**authorization:** `typing.Optional[str]` — Use your API key for authentication, or alternatively generate a temporary token and pass it via the token query parameter.
+
+**Example:** `token %DEEPGRAM_API_KEY%` or `bearer %DEEPGRAM_TOKEN%`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
## Agent V1 Connect
client.agent.v1.connect(...)
@@ -6063,6 +6340,16 @@ asyncio.run(main())
-
+**`send_update_listen(message: AgentV1UpdateListen)`** — Update the agent's speech-to-text settings
+
+- `AgentV1UpdateListen(listen=AgentV1UpdateListenListen(provider=DeepgramListenProviderV2(...)))` — Modify STT configuration during conversation. The provider identity (type, version, model) must match the current session.
+
+
+
+
+
+-
+
**`send_update_prompt(message: AgentV1UpdatePrompt)`** — Update the agent's system prompt
- `AgentV1UpdatePrompt(prompt="...")` — Change the agent's behavior instructions
diff --git a/src/deepgram/__init__.py b/src/deepgram/__init__.py
index 3b9390d0..8e0e64c3 100644
--- a/src/deepgram/__init__.py
+++ b/src/deepgram/__init__.py
@@ -222,6 +222,11 @@
SpeakV1Response,
SpeakV1SampleRate,
SpeakV1Speed,
+ SpeakV2Encoding,
+ SpeakV2MipOptOut,
+ SpeakV2Model,
+ SpeakV2SampleRate,
+ SpeakV2Tag,
ThinkSettingsV1,
ThinkSettingsV1ContextLength,
ThinkSettingsV1Endpoint,
@@ -791,6 +796,11 @@
"SpeakV1Response": ".types",
"SpeakV1SampleRate": ".types",
"SpeakV1Speed": ".types",
+ "SpeakV2Encoding": ".types",
+ "SpeakV2MipOptOut": ".types",
+ "SpeakV2Model": ".types",
+ "SpeakV2SampleRate": ".types",
+ "SpeakV2Tag": ".types",
"ThinkSettingsV1": ".types",
"ThinkSettingsV1ContextLength": ".types",
"ThinkSettingsV1ContextLengthParams": ".requests",
@@ -1236,6 +1246,11 @@ def __dir__():
"SpeakV1Response",
"SpeakV1SampleRate",
"SpeakV1Speed",
+ "SpeakV2Encoding",
+ "SpeakV2MipOptOut",
+ "SpeakV2Model",
+ "SpeakV2SampleRate",
+ "SpeakV2Tag",
"ThinkSettingsV1",
"ThinkSettingsV1ContextLength",
"ThinkSettingsV1ContextLengthParams",
diff --git a/src/deepgram/agent/__init__.py b/src/deepgram/agent/__init__.py
index 3b753b62..18f12e7d 100644
--- a/src/deepgram/agent/__init__.py
+++ b/src/deepgram/agent/__init__.py
@@ -41,6 +41,8 @@
AgentV1InjectionRefusedParams,
AgentV1KeepAlive,
AgentV1KeepAliveParams,
+ AgentV1ListenUpdated,
+ AgentV1ListenUpdatedParams,
AgentV1PromptUpdated,
AgentV1PromptUpdatedParams,
AgentV1ReceiveFunctionCallResponse,
@@ -120,6 +122,10 @@
AgentV1SpeakUpdatedParams,
AgentV1ThinkUpdated,
AgentV1ThinkUpdatedParams,
+ AgentV1UpdateListen,
+ AgentV1UpdateListenListen,
+ AgentV1UpdateListenListenParams,
+ AgentV1UpdateListenParams,
AgentV1UpdatePrompt,
AgentV1UpdatePromptParams,
AgentV1UpdateSpeak,
@@ -175,6 +181,8 @@
"AgentV1InjectionRefusedParams": ".v1",
"AgentV1KeepAlive": ".v1",
"AgentV1KeepAliveParams": ".v1",
+ "AgentV1ListenUpdated": ".v1",
+ "AgentV1ListenUpdatedParams": ".v1",
"AgentV1PromptUpdated": ".v1",
"AgentV1PromptUpdatedParams": ".v1",
"AgentV1ReceiveFunctionCallResponse": ".v1",
@@ -254,6 +262,10 @@
"AgentV1SpeakUpdatedParams": ".v1",
"AgentV1ThinkUpdated": ".v1",
"AgentV1ThinkUpdatedParams": ".v1",
+ "AgentV1UpdateListen": ".v1",
+ "AgentV1UpdateListenListen": ".v1",
+ "AgentV1UpdateListenListenParams": ".v1",
+ "AgentV1UpdateListenParams": ".v1",
"AgentV1UpdatePrompt": ".v1",
"AgentV1UpdatePromptParams": ".v1",
"AgentV1UpdateSpeak": ".v1",
@@ -333,6 +345,8 @@ def __dir__():
"AgentV1InjectionRefusedParams",
"AgentV1KeepAlive",
"AgentV1KeepAliveParams",
+ "AgentV1ListenUpdated",
+ "AgentV1ListenUpdatedParams",
"AgentV1PromptUpdated",
"AgentV1PromptUpdatedParams",
"AgentV1ReceiveFunctionCallResponse",
@@ -412,6 +426,10 @@ def __dir__():
"AgentV1SpeakUpdatedParams",
"AgentV1ThinkUpdated",
"AgentV1ThinkUpdatedParams",
+ "AgentV1UpdateListen",
+ "AgentV1UpdateListenListen",
+ "AgentV1UpdateListenListenParams",
+ "AgentV1UpdateListenParams",
"AgentV1UpdatePrompt",
"AgentV1UpdatePromptParams",
"AgentV1UpdateSpeak",
diff --git a/src/deepgram/agent/v1/__init__.py b/src/deepgram/agent/v1/__init__.py
index 17be4a0f..80bc28fb 100644
--- a/src/deepgram/agent/v1/__init__.py
+++ b/src/deepgram/agent/v1/__init__.py
@@ -25,6 +25,7 @@
AgentV1InjectUserMessage,
AgentV1InjectionRefused,
AgentV1KeepAlive,
+ AgentV1ListenUpdated,
AgentV1PromptUpdated,
AgentV1ReceiveFunctionCallResponse,
AgentV1SendFunctionCallResponse,
@@ -67,6 +68,8 @@
AgentV1SettingsFlags,
AgentV1SpeakUpdated,
AgentV1ThinkUpdated,
+ AgentV1UpdateListen,
+ AgentV1UpdateListenListen,
AgentV1UpdatePrompt,
AgentV1UpdateSpeak,
AgentV1UpdateSpeakSpeak,
@@ -95,6 +98,7 @@
AgentV1InjectUserMessageParams,
AgentV1InjectionRefusedParams,
AgentV1KeepAliveParams,
+ AgentV1ListenUpdatedParams,
AgentV1PromptUpdatedParams,
AgentV1ReceiveFunctionCallResponseParams,
AgentV1SendFunctionCallResponseParams,
@@ -132,6 +136,8 @@
AgentV1SettingsParams,
AgentV1SpeakUpdatedParams,
AgentV1ThinkUpdatedParams,
+ AgentV1UpdateListenListenParams,
+ AgentV1UpdateListenParams,
AgentV1UpdatePromptParams,
AgentV1UpdateSpeakParams,
AgentV1UpdateSpeakSpeakParams,
@@ -177,6 +183,8 @@
"AgentV1InjectionRefusedParams": ".requests",
"AgentV1KeepAlive": ".types",
"AgentV1KeepAliveParams": ".requests",
+ "AgentV1ListenUpdated": ".types",
+ "AgentV1ListenUpdatedParams": ".requests",
"AgentV1PromptUpdated": ".types",
"AgentV1PromptUpdatedParams": ".requests",
"AgentV1ReceiveFunctionCallResponse": ".types",
@@ -256,6 +264,10 @@
"AgentV1SpeakUpdatedParams": ".requests",
"AgentV1ThinkUpdated": ".types",
"AgentV1ThinkUpdatedParams": ".requests",
+ "AgentV1UpdateListen": ".types",
+ "AgentV1UpdateListenListen": ".types",
+ "AgentV1UpdateListenListenParams": ".requests",
+ "AgentV1UpdateListenParams": ".requests",
"AgentV1UpdatePrompt": ".types",
"AgentV1UpdatePromptParams": ".requests",
"AgentV1UpdateSpeak": ".types",
@@ -335,6 +347,8 @@ def __dir__():
"AgentV1InjectionRefusedParams",
"AgentV1KeepAlive",
"AgentV1KeepAliveParams",
+ "AgentV1ListenUpdated",
+ "AgentV1ListenUpdatedParams",
"AgentV1PromptUpdated",
"AgentV1PromptUpdatedParams",
"AgentV1ReceiveFunctionCallResponse",
@@ -414,6 +428,10 @@ def __dir__():
"AgentV1SpeakUpdatedParams",
"AgentV1ThinkUpdated",
"AgentV1ThinkUpdatedParams",
+ "AgentV1UpdateListen",
+ "AgentV1UpdateListenListen",
+ "AgentV1UpdateListenListenParams",
+ "AgentV1UpdateListenParams",
"AgentV1UpdatePrompt",
"AgentV1UpdatePromptParams",
"AgentV1UpdateSpeak",
diff --git a/src/deepgram/agent/v1/requests/__init__.py b/src/deepgram/agent/v1/requests/__init__.py
index af4de477..eb841695 100644
--- a/src/deepgram/agent/v1/requests/__init__.py
+++ b/src/deepgram/agent/v1/requests/__init__.py
@@ -21,6 +21,7 @@
from .agent_v1inject_user_message import AgentV1InjectUserMessageParams
from .agent_v1injection_refused import AgentV1InjectionRefusedParams
from .agent_v1keep_alive import AgentV1KeepAliveParams
+ from .agent_v1listen_updated import AgentV1ListenUpdatedParams
from .agent_v1prompt_updated import AgentV1PromptUpdatedParams
from .agent_v1receive_function_call_response import AgentV1ReceiveFunctionCallResponseParams
from .agent_v1send_function_call_response import AgentV1SendFunctionCallResponseParams
@@ -74,6 +75,8 @@
from .agent_v1settings_flags import AgentV1SettingsFlagsParams
from .agent_v1speak_updated import AgentV1SpeakUpdatedParams
from .agent_v1think_updated import AgentV1ThinkUpdatedParams
+ from .agent_v1update_listen import AgentV1UpdateListenParams
+ from .agent_v1update_listen_listen import AgentV1UpdateListenListenParams
from .agent_v1update_prompt import AgentV1UpdatePromptParams
from .agent_v1update_speak import AgentV1UpdateSpeakParams
from .agent_v1update_speak_speak import AgentV1UpdateSpeakSpeakParams
@@ -100,6 +103,7 @@
"AgentV1InjectUserMessageParams": ".agent_v1inject_user_message",
"AgentV1InjectionRefusedParams": ".agent_v1injection_refused",
"AgentV1KeepAliveParams": ".agent_v1keep_alive",
+ "AgentV1ListenUpdatedParams": ".agent_v1listen_updated",
"AgentV1PromptUpdatedParams": ".agent_v1prompt_updated",
"AgentV1ReceiveFunctionCallResponseParams": ".agent_v1receive_function_call_response",
"AgentV1SendFunctionCallResponseParams": ".agent_v1send_function_call_response",
@@ -137,6 +141,8 @@
"AgentV1SettingsParams": ".agent_v1settings",
"AgentV1SpeakUpdatedParams": ".agent_v1speak_updated",
"AgentV1ThinkUpdatedParams": ".agent_v1think_updated",
+ "AgentV1UpdateListenListenParams": ".agent_v1update_listen_listen",
+ "AgentV1UpdateListenParams": ".agent_v1update_listen",
"AgentV1UpdatePromptParams": ".agent_v1update_prompt",
"AgentV1UpdateSpeakParams": ".agent_v1update_speak",
"AgentV1UpdateSpeakSpeakParams": ".agent_v1update_speak_speak",
@@ -187,6 +193,7 @@ def __dir__():
"AgentV1InjectUserMessageParams",
"AgentV1InjectionRefusedParams",
"AgentV1KeepAliveParams",
+ "AgentV1ListenUpdatedParams",
"AgentV1PromptUpdatedParams",
"AgentV1ReceiveFunctionCallResponseParams",
"AgentV1SendFunctionCallResponseParams",
@@ -224,6 +231,8 @@ def __dir__():
"AgentV1SettingsParams",
"AgentV1SpeakUpdatedParams",
"AgentV1ThinkUpdatedParams",
+ "AgentV1UpdateListenListenParams",
+ "AgentV1UpdateListenParams",
"AgentV1UpdatePromptParams",
"AgentV1UpdateSpeakParams",
"AgentV1UpdateSpeakSpeakParams",
diff --git a/src/deepgram/agent/v1/requests/agent_v1listen_updated.py b/src/deepgram/agent/v1/requests/agent_v1listen_updated.py
new file mode 100644
index 00000000..de0f80c7
--- /dev/null
+++ b/src/deepgram/agent/v1/requests/agent_v1listen_updated.py
@@ -0,0 +1,12 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+
+
+class AgentV1ListenUpdatedParams(typing_extensions.TypedDict):
+ type: typing.Literal["ListenUpdated"]
+ """
+ Message type identifier for listen update confirmation
+ """
diff --git a/src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider.py b/src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider.py
index 98381492..fa1dabfa 100644
--- a/src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider.py
+++ b/src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider.py
@@ -21,6 +21,9 @@ class AgentV1SettingsAgentContextListenProvider_V2Params(typing_extensions.Typed
type: typing.Literal["deepgram"]
model: str
language_hints: typing_extensions.NotRequired[typing.Sequence[str]]
+ eot_threshold: typing_extensions.NotRequired[float]
+ eager_eot_threshold: typing_extensions.NotRequired[float]
+ eot_timeout_ms: typing_extensions.NotRequired[int]
keyterms: typing_extensions.NotRequired[typing.Sequence[str]]
diff --git a/src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider.py b/src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider.py
index a0ddf06b..cc5ec701 100644
--- a/src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider.py
+++ b/src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider.py
@@ -21,6 +21,9 @@ class AgentV1SettingsAgentListenProvider_V2Params(typing_extensions.TypedDict):
type: typing.Literal["deepgram"]
model: str
language_hints: typing_extensions.NotRequired[typing.Sequence[str]]
+ eot_threshold: typing_extensions.NotRequired[float]
+ eager_eot_threshold: typing_extensions.NotRequired[float]
+ eot_timeout_ms: typing_extensions.NotRequired[int]
keyterms: typing_extensions.NotRequired[typing.Sequence[str]]
diff --git a/src/deepgram/agent/v1/requests/agent_v1update_listen.py b/src/deepgram/agent/v1/requests/agent_v1update_listen.py
new file mode 100644
index 00000000..7309bc9e
--- /dev/null
+++ b/src/deepgram/agent/v1/requests/agent_v1update_listen.py
@@ -0,0 +1,18 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+from .agent_v1update_listen_listen import AgentV1UpdateListenListenParams
+
+
+class AgentV1UpdateListenParams(typing_extensions.TypedDict):
+ type: typing.Literal["UpdateListen"]
+ """
+ Message type identifier for updating the listen configuration
+ """
+
+ listen: AgentV1UpdateListenListenParams
+ """
+ Listen configuration to update. Contains a provider object with the same schema as Settings. The provider identity (type, version, model) is required and must match the current session.
+ """
diff --git a/src/deepgram/agent/v1/requests/agent_v1update_listen_listen.py b/src/deepgram/agent/v1/requests/agent_v1update_listen_listen.py
new file mode 100644
index 00000000..2a91d9f5
--- /dev/null
+++ b/src/deepgram/agent/v1/requests/agent_v1update_listen_listen.py
@@ -0,0 +1,12 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing_extensions
+from ....requests.deepgram_listen_provider_v2 import DeepgramListenProviderV2Params
+
+
+class AgentV1UpdateListenListenParams(typing_extensions.TypedDict):
+ """
+ Listen configuration to update. Contains a provider object with the same schema as Settings. The provider identity (type, version, model) is required and must match the current session.
+ """
+
+ provider: DeepgramListenProviderV2Params
diff --git a/src/deepgram/agent/v1/socket_client.py b/src/deepgram/agent/v1/socket_client.py
index 17c3d0d2..cc5437c2 100644
--- a/src/deepgram/agent/v1/socket_client.py
+++ b/src/deepgram/agent/v1/socket_client.py
@@ -18,6 +18,7 @@
from .types.agent_v1inject_user_message import AgentV1InjectUserMessage
from .types.agent_v1injection_refused import AgentV1InjectionRefused
from .types.agent_v1keep_alive import AgentV1KeepAlive
+from .types.agent_v1listen_updated import AgentV1ListenUpdated
from .types.agent_v1prompt_updated import AgentV1PromptUpdated
from .types.agent_v1receive_function_call_response import AgentV1ReceiveFunctionCallResponse
from .types.agent_v1send_function_call_response import AgentV1SendFunctionCallResponse
@@ -25,6 +26,7 @@
from .types.agent_v1settings_applied import AgentV1SettingsApplied
from .types.agent_v1speak_updated import AgentV1SpeakUpdated
from .types.agent_v1think_updated import AgentV1ThinkUpdated
+from .types.agent_v1update_listen import AgentV1UpdateListen
from .types.agent_v1update_prompt import AgentV1UpdatePrompt
from .types.agent_v1update_speak import AgentV1UpdateSpeak
from .types.agent_v1update_think import AgentV1UpdateThink
@@ -37,8 +39,6 @@
except ImportError:
from websockets import WebSocketClientProtocol # type: ignore
-_logger = logging.getLogger(__name__)
-
def _sanitize_numeric_types(obj: typing.Any) -> typing.Any:
"""
@@ -60,11 +60,13 @@ def _sanitize_numeric_types(obj: typing.Any) -> typing.Any:
return obj
+_logger = logging.getLogger(__name__)
V1SocketClientResponse = typing.Union[
+ AgentV1ListenUpdated,
+ AgentV1ThinkUpdated,
AgentV1ReceiveFunctionCallResponse,
AgentV1PromptUpdated,
AgentV1SpeakUpdated,
- AgentV1ThinkUpdated,
AgentV1InjectionRefused,
AgentV1Welcome,
AgentV1SettingsApplied,
@@ -136,6 +138,20 @@ async def send_settings(self, message: AgentV1Settings) -> None:
"""
await self._send_model(message)
+ async def send_update_listen(self, message: AgentV1UpdateListen) -> None:
+ """
+ Send a message to the websocket connection.
+ The message will be sent as a AgentV1UpdateListen.
+ """
+ await self._send_model(message)
+
+ async def send_update_think(self, message: AgentV1UpdateThink) -> None:
+ """
+ Send a message to the websocket connection.
+ The message will be sent as a AgentV1UpdateThink.
+ """
+ await self._send_model(message)
+
async def send_update_speak(self, message: AgentV1UpdateSpeak) -> None:
"""
Send a message to the websocket connection.
@@ -178,13 +194,6 @@ async def send_update_prompt(self, message: AgentV1UpdatePrompt) -> None:
"""
await self._send_model(message)
- async def send_update_think(self, message: AgentV1UpdateThink) -> None:
- """
- Send a message to the websocket connection.
- The message will be sent as a AgentV1UpdateThink.
- """
- await self._send_model(message)
-
async def send_media(self, message: bytes) -> None:
"""
Send a message to the websocket connection.
@@ -276,6 +285,20 @@ def send_settings(self, message: AgentV1Settings) -> None:
"""
self._send_model(message)
+ def send_update_listen(self, message: AgentV1UpdateListen) -> None:
+ """
+ Send a message to the websocket connection.
+ The message will be sent as a AgentV1UpdateListen.
+ """
+ self._send_model(message)
+
+ def send_update_think(self, message: AgentV1UpdateThink) -> None:
+ """
+ Send a message to the websocket connection.
+ The message will be sent as a AgentV1UpdateThink.
+ """
+ self._send_model(message)
+
def send_update_speak(self, message: AgentV1UpdateSpeak) -> None:
"""
Send a message to the websocket connection.
@@ -318,13 +341,6 @@ def send_update_prompt(self, message: AgentV1UpdatePrompt) -> None:
"""
self._send_model(message)
- def send_update_think(self, message: AgentV1UpdateThink) -> None:
- """
- Send a message to the websocket connection.
- The message will be sent as a AgentV1UpdateThink.
- """
- self._send_model(message)
-
def send_media(self, message: bytes) -> None:
"""
Send a message to the websocket connection.
diff --git a/src/deepgram/agent/v1/types/__init__.py b/src/deepgram/agent/v1/types/__init__.py
index d06917e2..91fdd222 100644
--- a/src/deepgram/agent/v1/types/__init__.py
+++ b/src/deepgram/agent/v1/types/__init__.py
@@ -24,6 +24,7 @@
from .agent_v1inject_user_message import AgentV1InjectUserMessage
from .agent_v1injection_refused import AgentV1InjectionRefused
from .agent_v1keep_alive import AgentV1KeepAlive
+ from .agent_v1listen_updated import AgentV1ListenUpdated
from .agent_v1prompt_updated import AgentV1PromptUpdated
from .agent_v1receive_function_call_response import AgentV1ReceiveFunctionCallResponse
from .agent_v1send_function_call_response import AgentV1SendFunctionCallResponse
@@ -82,6 +83,8 @@
from .agent_v1settings_flags import AgentV1SettingsFlags
from .agent_v1speak_updated import AgentV1SpeakUpdated
from .agent_v1think_updated import AgentV1ThinkUpdated
+ from .agent_v1update_listen import AgentV1UpdateListen
+ from .agent_v1update_listen_listen import AgentV1UpdateListenListen
from .agent_v1update_prompt import AgentV1UpdatePrompt
from .agent_v1update_speak import AgentV1UpdateSpeak
from .agent_v1update_speak_speak import AgentV1UpdateSpeakSpeak
@@ -111,6 +114,7 @@
"AgentV1InjectUserMessage": ".agent_v1inject_user_message",
"AgentV1InjectionRefused": ".agent_v1injection_refused",
"AgentV1KeepAlive": ".agent_v1keep_alive",
+ "AgentV1ListenUpdated": ".agent_v1listen_updated",
"AgentV1PromptUpdated": ".agent_v1prompt_updated",
"AgentV1ReceiveFunctionCallResponse": ".agent_v1receive_function_call_response",
"AgentV1SendFunctionCallResponse": ".agent_v1send_function_call_response",
@@ -153,6 +157,8 @@
"AgentV1SettingsFlags": ".agent_v1settings_flags",
"AgentV1SpeakUpdated": ".agent_v1speak_updated",
"AgentV1ThinkUpdated": ".agent_v1think_updated",
+ "AgentV1UpdateListen": ".agent_v1update_listen",
+ "AgentV1UpdateListenListen": ".agent_v1update_listen_listen",
"AgentV1UpdatePrompt": ".agent_v1update_prompt",
"AgentV1UpdateSpeak": ".agent_v1update_speak",
"AgentV1UpdateSpeakSpeak": ".agent_v1update_speak_speak",
@@ -206,6 +212,7 @@ def __dir__():
"AgentV1InjectUserMessage",
"AgentV1InjectionRefused",
"AgentV1KeepAlive",
+ "AgentV1ListenUpdated",
"AgentV1PromptUpdated",
"AgentV1ReceiveFunctionCallResponse",
"AgentV1SendFunctionCallResponse",
@@ -248,6 +255,8 @@ def __dir__():
"AgentV1SettingsFlags",
"AgentV1SpeakUpdated",
"AgentV1ThinkUpdated",
+ "AgentV1UpdateListen",
+ "AgentV1UpdateListenListen",
"AgentV1UpdatePrompt",
"AgentV1UpdateSpeak",
"AgentV1UpdateSpeakSpeak",
diff --git a/src/deepgram/agent/v1/types/agent_v1listen_updated.py b/src/deepgram/agent/v1/types/agent_v1listen_updated.py
new file mode 100644
index 00000000..9c3ea1b7
--- /dev/null
+++ b/src/deepgram/agent/v1/types/agent_v1listen_updated.py
@@ -0,0 +1,23 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class AgentV1ListenUpdated(UncheckedBaseModel):
+ type: typing.Literal["ListenUpdated"] = pydantic.Field(default="ListenUpdated")
+ """
+ Message type identifier for listen update confirmation
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider.py b/src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider.py
index 229a54dd..13d6739a 100644
--- a/src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider.py
+++ b/src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider.py
@@ -34,6 +34,9 @@ class AgentV1SettingsAgentContextListenProvider_V2(UncheckedBaseModel):
model: str
language_hints: typing.Optional[typing.List[str]] = None
language_hint: typing.Optional[typing.Union[str, typing.List[str]]] = pydantic.Field(default=None, exclude=True)
+ eot_threshold: typing.Optional[float] = None
+ eager_eot_threshold: typing.Optional[float] = None
+ eot_timeout_ms: typing.Optional[int] = None
keyterms: typing.Optional[typing.List[str]] = None
# Backward-compat: the public field was historically named `language_hint`
diff --git a/src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider.py b/src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider.py
index 7fe885cb..9bc63301 100644
--- a/src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider.py
+++ b/src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider.py
@@ -34,6 +34,9 @@ class AgentV1SettingsAgentListenProvider_V2(UncheckedBaseModel):
model: str
language_hints: typing.Optional[typing.List[str]] = None
language_hint: typing.Optional[typing.Union[str, typing.List[str]]] = pydantic.Field(default=None, exclude=True)
+ eot_threshold: typing.Optional[float] = None
+ eager_eot_threshold: typing.Optional[float] = None
+ eot_timeout_ms: typing.Optional[int] = None
keyterms: typing.Optional[typing.List[str]] = None
# Backward-compat: the public field was historically named `language_hint`
diff --git a/src/deepgram/agent/v1/types/agent_v1update_listen.py b/src/deepgram/agent/v1/types/agent_v1update_listen.py
new file mode 100644
index 00000000..a25a4ae1
--- /dev/null
+++ b/src/deepgram/agent/v1/types/agent_v1update_listen.py
@@ -0,0 +1,29 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .agent_v1update_listen_listen import AgentV1UpdateListenListen
+
+
+class AgentV1UpdateListen(UncheckedBaseModel):
+ type: typing.Literal["UpdateListen"] = pydantic.Field(default="UpdateListen")
+ """
+ Message type identifier for updating the listen configuration
+ """
+
+ listen: AgentV1UpdateListenListen = pydantic.Field()
+ """
+ Listen configuration to update. Contains a provider object with the same schema as Settings. The provider identity (type, version, model) is required and must match the current session.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/agent/v1/types/agent_v1update_listen_listen.py b/src/deepgram/agent/v1/types/agent_v1update_listen_listen.py
new file mode 100644
index 00000000..386483eb
--- /dev/null
+++ b/src/deepgram/agent/v1/types/agent_v1update_listen_listen.py
@@ -0,0 +1,25 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from ....types.deepgram_listen_provider_v2 import DeepgramListenProviderV2
+
+
+class AgentV1UpdateListenListen(UncheckedBaseModel):
+ """
+ Listen configuration to update. Contains a provider object with the same schema as Settings. The provider identity (type, version, model) is required and must match the current session.
+ """
+
+ provider: DeepgramListenProviderV2
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/core/client_wrapper.py b/src/deepgram/core/client_wrapper.py
index 5136a051..ee926880 100644
--- a/src/deepgram/core/client_wrapper.py
+++ b/src/deepgram/core/client_wrapper.py
@@ -30,12 +30,12 @@ def get_headers(self) -> typing.Dict[str, str]:
import platform
headers: typing.Dict[str, str] = {
- "User-Agent": "deepgram-sdk/7.3.2",
+ "User-Agent": "deepgram-sdk/7.4.1",
"X-Fern-Language": "Python",
"X-Fern-Runtime": f"python/{platform.python_version()}",
"X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}",
"X-Fern-SDK-Name": "deepgram-sdk",
- "X-Fern-SDK-Version": "7.3.2",
+ "X-Fern-SDK-Version": "7.4.1",
**(self.get_custom_headers() or {}),
}
headers["Authorization"] = f"Token {self.api_key}"
diff --git a/src/deepgram/requests/deepgram_listen_provider_v2.py b/src/deepgram/requests/deepgram_listen_provider_v2.py
index 0f094f5a..47a41ed7 100644
--- a/src/deepgram/requests/deepgram_listen_provider_v2.py
+++ b/src/deepgram/requests/deepgram_listen_provider_v2.py
@@ -26,6 +26,21 @@ class DeepgramListenProviderV2Params(typing_extensions.TypedDict):
An array of one or more BCP-47 language codes to bias the model toward specific languages. Only supported when model is flux-general-multi. Without hints, the model auto-detects the spoken language. See the Language Prompting guide for details.
"""
+ eot_threshold: typing_extensions.NotRequired[float]
+ """
+ End-of-turn confidence required to finish a turn. Valid range: 0.5 - 0.9. Defaults to 0.7.
+ """
+
+ eager_eot_threshold: typing_extensions.NotRequired[float]
+ """
+ End-of-turn confidence required to fire an eager end-of-turn event. When set, enables EagerEndOfTurn and TurnResumed events. Valid range: 0.3 - 0.9.
+ """
+
+ eot_timeout_ms: typing_extensions.NotRequired[int]
+ """
+ A turn will be finished when this much time in milliseconds has passed after speech, regardless of EOT confidence. Defaults to 5000.
+ """
+
keyterms: typing_extensions.NotRequired[typing.Sequence[str]]
"""
Prompt keyterm recognition to improve Keyword Recall Rate
diff --git a/src/deepgram/speak/__init__.py b/src/deepgram/speak/__init__.py
index 73eda24c..27653587 100644
--- a/src/deepgram/speak/__init__.py
+++ b/src/deepgram/speak/__init__.py
@@ -6,7 +6,7 @@
from importlib import import_module
if typing.TYPE_CHECKING:
- from . import v1
+ from . import v1, v2
from .v1 import (
SpeakV1Clear,
SpeakV1ClearParams,
@@ -30,6 +30,31 @@
SpeakV1Warning,
SpeakV1WarningParams,
)
+ from .v2 import (
+ SpeakV2Close,
+ SpeakV2CloseParams,
+ SpeakV2Connected,
+ SpeakV2ConnectedParams,
+ SpeakV2Error,
+ SpeakV2ErrorCode,
+ SpeakV2ErrorParams,
+ SpeakV2Flush,
+ SpeakV2FlushParams,
+ SpeakV2Flushed,
+ SpeakV2FlushedParams,
+ SpeakV2SessionMetadata,
+ SpeakV2SessionMetadataParams,
+ SpeakV2Speak,
+ SpeakV2SpeakParams,
+ SpeakV2SpeechMetadata,
+ SpeakV2SpeechMetadataControlsApplied,
+ SpeakV2SpeechMetadataControlsAppliedParams,
+ SpeakV2SpeechMetadataParams,
+ SpeakV2SpeechStarted,
+ SpeakV2SpeechStartedParams,
+ SpeakV2Warning,
+ SpeakV2WarningParams,
+ )
_dynamic_imports: typing.Dict[str, str] = {
"SpeakV1Clear": ".v1",
"SpeakV1ClearParams": ".v1",
@@ -52,7 +77,31 @@
"SpeakV1TextParams": ".v1",
"SpeakV1Warning": ".v1",
"SpeakV1WarningParams": ".v1",
+ "SpeakV2Close": ".v2",
+ "SpeakV2CloseParams": ".v2",
+ "SpeakV2Connected": ".v2",
+ "SpeakV2ConnectedParams": ".v2",
+ "SpeakV2Error": ".v2",
+ "SpeakV2ErrorCode": ".v2",
+ "SpeakV2ErrorParams": ".v2",
+ "SpeakV2Flush": ".v2",
+ "SpeakV2FlushParams": ".v2",
+ "SpeakV2Flushed": ".v2",
+ "SpeakV2FlushedParams": ".v2",
+ "SpeakV2SessionMetadata": ".v2",
+ "SpeakV2SessionMetadataParams": ".v2",
+ "SpeakV2Speak": ".v2",
+ "SpeakV2SpeakParams": ".v2",
+ "SpeakV2SpeechMetadata": ".v2",
+ "SpeakV2SpeechMetadataControlsApplied": ".v2",
+ "SpeakV2SpeechMetadataControlsAppliedParams": ".v2",
+ "SpeakV2SpeechMetadataParams": ".v2",
+ "SpeakV2SpeechStarted": ".v2",
+ "SpeakV2SpeechStartedParams": ".v2",
+ "SpeakV2Warning": ".v2",
+ "SpeakV2WarningParams": ".v2",
"v1": ".v1",
+ "v2": ".v2",
}
@@ -99,5 +148,29 @@ def __dir__():
"SpeakV1TextParams",
"SpeakV1Warning",
"SpeakV1WarningParams",
+ "SpeakV2Close",
+ "SpeakV2CloseParams",
+ "SpeakV2Connected",
+ "SpeakV2ConnectedParams",
+ "SpeakV2Error",
+ "SpeakV2ErrorCode",
+ "SpeakV2ErrorParams",
+ "SpeakV2Flush",
+ "SpeakV2FlushParams",
+ "SpeakV2Flushed",
+ "SpeakV2FlushedParams",
+ "SpeakV2SessionMetadata",
+ "SpeakV2SessionMetadataParams",
+ "SpeakV2Speak",
+ "SpeakV2SpeakParams",
+ "SpeakV2SpeechMetadata",
+ "SpeakV2SpeechMetadataControlsApplied",
+ "SpeakV2SpeechMetadataControlsAppliedParams",
+ "SpeakV2SpeechMetadataParams",
+ "SpeakV2SpeechStarted",
+ "SpeakV2SpeechStartedParams",
+ "SpeakV2Warning",
+ "SpeakV2WarningParams",
"v1",
+ "v2",
]
diff --git a/src/deepgram/speak/client.py b/src/deepgram/speak/client.py
index 4efefd5f..7ca366a2 100644
--- a/src/deepgram/speak/client.py
+++ b/src/deepgram/speak/client.py
@@ -9,6 +9,7 @@
if typing.TYPE_CHECKING:
from .v1.client import AsyncV1Client, V1Client
+ from .v2.client import AsyncV2Client, V2Client
class SpeakClient:
@@ -16,6 +17,7 @@ def __init__(self, *, client_wrapper: SyncClientWrapper):
self._raw_client = RawSpeakClient(client_wrapper=client_wrapper)
self._client_wrapper = client_wrapper
self._v1: typing.Optional[V1Client] = None
+ self._v2: typing.Optional[V2Client] = None
@property
def with_raw_response(self) -> RawSpeakClient:
@@ -36,12 +38,21 @@ def v1(self):
self._v1 = V1Client(client_wrapper=self._client_wrapper)
return self._v1
+ @property
+ def v2(self):
+ if self._v2 is None:
+ from .v2.client import V2Client # noqa: E402
+
+ self._v2 = V2Client(client_wrapper=self._client_wrapper)
+ return self._v2
+
class AsyncSpeakClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
self._raw_client = AsyncRawSpeakClient(client_wrapper=client_wrapper)
self._client_wrapper = client_wrapper
self._v1: typing.Optional[AsyncV1Client] = None
+ self._v2: typing.Optional[AsyncV2Client] = None
@property
def with_raw_response(self) -> AsyncRawSpeakClient:
@@ -61,3 +72,11 @@ def v1(self):
self._v1 = AsyncV1Client(client_wrapper=self._client_wrapper)
return self._v1
+
+ @property
+ def v2(self):
+ if self._v2 is None:
+ from .v2.client import AsyncV2Client # noqa: E402
+
+ self._v2 = AsyncV2Client(client_wrapper=self._client_wrapper)
+ return self._v2
diff --git a/src/deepgram/speak/v2/__init__.py b/src/deepgram/speak/v2/__init__.py
new file mode 100644
index 00000000..707598c6
--- /dev/null
+++ b/src/deepgram/speak/v2/__init__.py
@@ -0,0 +1,108 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .types import (
+ SpeakV2Close,
+ SpeakV2Connected,
+ SpeakV2Error,
+ SpeakV2ErrorCode,
+ SpeakV2Flush,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Speak,
+ SpeakV2SpeechMetadata,
+ SpeakV2SpeechMetadataControlsApplied,
+ SpeakV2SpeechStarted,
+ SpeakV2Warning,
+ )
+ from .requests import (
+ SpeakV2CloseParams,
+ SpeakV2ConnectedParams,
+ SpeakV2ErrorParams,
+ SpeakV2FlushParams,
+ SpeakV2FlushedParams,
+ SpeakV2SessionMetadataParams,
+ SpeakV2SpeakParams,
+ SpeakV2SpeechMetadataControlsAppliedParams,
+ SpeakV2SpeechMetadataParams,
+ SpeakV2SpeechStartedParams,
+ SpeakV2WarningParams,
+ )
+_dynamic_imports: typing.Dict[str, str] = {
+ "SpeakV2Close": ".types",
+ "SpeakV2CloseParams": ".requests",
+ "SpeakV2Connected": ".types",
+ "SpeakV2ConnectedParams": ".requests",
+ "SpeakV2Error": ".types",
+ "SpeakV2ErrorCode": ".types",
+ "SpeakV2ErrorParams": ".requests",
+ "SpeakV2Flush": ".types",
+ "SpeakV2FlushParams": ".requests",
+ "SpeakV2Flushed": ".types",
+ "SpeakV2FlushedParams": ".requests",
+ "SpeakV2SessionMetadata": ".types",
+ "SpeakV2SessionMetadataParams": ".requests",
+ "SpeakV2Speak": ".types",
+ "SpeakV2SpeakParams": ".requests",
+ "SpeakV2SpeechMetadata": ".types",
+ "SpeakV2SpeechMetadataControlsApplied": ".types",
+ "SpeakV2SpeechMetadataControlsAppliedParams": ".requests",
+ "SpeakV2SpeechMetadataParams": ".requests",
+ "SpeakV2SpeechStarted": ".types",
+ "SpeakV2SpeechStartedParams": ".requests",
+ "SpeakV2Warning": ".types",
+ "SpeakV2WarningParams": ".requests",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "SpeakV2Close",
+ "SpeakV2CloseParams",
+ "SpeakV2Connected",
+ "SpeakV2ConnectedParams",
+ "SpeakV2Error",
+ "SpeakV2ErrorCode",
+ "SpeakV2ErrorParams",
+ "SpeakV2Flush",
+ "SpeakV2FlushParams",
+ "SpeakV2Flushed",
+ "SpeakV2FlushedParams",
+ "SpeakV2SessionMetadata",
+ "SpeakV2SessionMetadataParams",
+ "SpeakV2Speak",
+ "SpeakV2SpeakParams",
+ "SpeakV2SpeechMetadata",
+ "SpeakV2SpeechMetadataControlsApplied",
+ "SpeakV2SpeechMetadataControlsAppliedParams",
+ "SpeakV2SpeechMetadataParams",
+ "SpeakV2SpeechStarted",
+ "SpeakV2SpeechStartedParams",
+ "SpeakV2Warning",
+ "SpeakV2WarningParams",
+]
diff --git a/src/deepgram/speak/v2/client.py b/src/deepgram/speak/v2/client.py
new file mode 100644
index 00000000..ed0c54a7
--- /dev/null
+++ b/src/deepgram/speak/v2/client.py
@@ -0,0 +1,226 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+import urllib.parse
+from contextlib import asynccontextmanager, contextmanager
+
+import websockets.sync.client as websockets_sync_client
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.jsonable_encoder import jsonable_encoder
+from ...core.query_encoder import encode_query
+from ...core.remove_none_from_dict import remove_none_from_dict
+from ...core.request_options import RequestOptions
+from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code
+from ...types.speak_v2encoding import SpeakV2Encoding
+from ...types.speak_v2mip_opt_out import SpeakV2MipOptOut
+from ...types.speak_v2model import SpeakV2Model
+from ...types.speak_v2sample_rate import SpeakV2SampleRate
+from ...types.speak_v2tag import SpeakV2Tag
+from .raw_client import AsyncRawV2Client, RawV2Client
+from .socket_client import AsyncV2SocketClient, V2SocketClient
+
+try:
+ from websockets.legacy.client import connect as websockets_client_connect # type: ignore
+except ImportError:
+ from websockets import connect as websockets_client_connect # type: ignore
+
+
+class V2Client:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._raw_client = RawV2Client(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> RawV2Client:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ RawV2Client
+ """
+ return self._raw_client
+
+ @contextmanager
+ def connect(
+ self,
+ *,
+ model: SpeakV2Model,
+ encoding: typing.Optional[SpeakV2Encoding] = None,
+ sample_rate: typing.Optional[SpeakV2SampleRate] = None,
+ mip_opt_out: typing.Optional[SpeakV2MipOptOut] = None,
+ tag: typing.Optional[SpeakV2Tag] = None,
+ authorization: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> typing.Iterator[V2SocketClient]:
+ """
+ Streaming, turn-based text-to-speech (Flux TTS) built for voice-agent
+ pipelines. Stream LLM tokens in, speak them to the user, and report
+ per-turn billing and timing.
+
+ Parameters
+ ----------
+ model : SpeakV2Model
+
+ encoding : typing.Optional[SpeakV2Encoding]
+
+ sample_rate : typing.Optional[SpeakV2SampleRate]
+
+ mip_opt_out : typing.Optional[SpeakV2MipOptOut]
+
+ tag : typing.Optional[SpeakV2Tag]
+
+ authorization : typing.Optional[str]
+ Use your API key or a [temporary token](/guides/fundamentals/token-based-authentication) for authentication via the `Authorization` header. In client-side environments where custom headers are not supported, use the [`Sec-WebSocket-Protocol`](/guides/deep-dives/using-the-sec-websocket-protocol) header instead.
+
+ **Example:** `Authorization: Token %DEEPGRAM_API_KEY%` or `Authorization: Bearer %DEEPGRAM_TOKEN%`
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ V2SocketClient
+ """
+ ws_url = self._raw_client._client_wrapper.get_environment().production + "/v2/speak"
+ _encoded_query_params = encode_query(
+ jsonable_encoder(
+ remove_none_from_dict(
+ {
+ "model": model,
+ "encoding": encoding,
+ "sample_rate": sample_rate,
+ "mip_opt_out": mip_opt_out,
+ "tag": tag,
+ **(
+ request_options.get("additional_query_parameters", {}) or {}
+ if request_options is not None
+ else {}
+ ),
+ }
+ )
+ )
+ )
+ if _encoded_query_params:
+ ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
+ headers = self._raw_client._client_wrapper.get_headers()
+ if authorization is not None:
+ headers["Authorization"] = str(authorization)
+ if request_options and "additional_headers" in request_options:
+ headers.update(request_options["additional_headers"])
+ try:
+ with websockets_sync_client.connect(ws_url, additional_headers=headers) as protocol:
+ yield V2SocketClient(websocket=protocol)
+ except InvalidWebSocketStatus as exc:
+ status_code: int = get_status_code(exc)
+ if status_code == 401:
+ raise ApiError(
+ status_code=status_code,
+ headers=dict(headers),
+ body="Websocket initialized with invalid credentials.",
+ )
+ raise ApiError(
+ status_code=status_code,
+ headers=dict(headers),
+ body="Unexpected error when initializing websocket connection.",
+ )
+
+
+class AsyncV2Client:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._raw_client = AsyncRawV2Client(client_wrapper=client_wrapper)
+
+ @property
+ def with_raw_response(self) -> AsyncRawV2Client:
+ """
+ Retrieves a raw implementation of this client that returns raw responses.
+
+ Returns
+ -------
+ AsyncRawV2Client
+ """
+ return self._raw_client
+
+ @asynccontextmanager
+ async def connect(
+ self,
+ *,
+ model: SpeakV2Model,
+ encoding: typing.Optional[SpeakV2Encoding] = None,
+ sample_rate: typing.Optional[SpeakV2SampleRate] = None,
+ mip_opt_out: typing.Optional[SpeakV2MipOptOut] = None,
+ tag: typing.Optional[SpeakV2Tag] = None,
+ authorization: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> typing.AsyncIterator[AsyncV2SocketClient]:
+ """
+ Streaming, turn-based text-to-speech (Flux TTS) built for voice-agent
+ pipelines. Stream LLM tokens in, speak them to the user, and report
+ per-turn billing and timing.
+
+ Parameters
+ ----------
+ model : SpeakV2Model
+
+ encoding : typing.Optional[SpeakV2Encoding]
+
+ sample_rate : typing.Optional[SpeakV2SampleRate]
+
+ mip_opt_out : typing.Optional[SpeakV2MipOptOut]
+
+ tag : typing.Optional[SpeakV2Tag]
+
+ authorization : typing.Optional[str]
+ Use your API key or a [temporary token](/guides/fundamentals/token-based-authentication) for authentication via the `Authorization` header. In client-side environments where custom headers are not supported, use the [`Sec-WebSocket-Protocol`](/guides/deep-dives/using-the-sec-websocket-protocol) header instead.
+
+ **Example:** `Authorization: Token %DEEPGRAM_API_KEY%` or `Authorization: Bearer %DEEPGRAM_TOKEN%`
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncV2SocketClient
+ """
+ ws_url = self._raw_client._client_wrapper.get_environment().production + "/v2/speak"
+ _encoded_query_params = encode_query(
+ jsonable_encoder(
+ remove_none_from_dict(
+ {
+ "model": model,
+ "encoding": encoding,
+ "sample_rate": sample_rate,
+ "mip_opt_out": mip_opt_out,
+ "tag": tag,
+ **(
+ request_options.get("additional_query_parameters", {}) or {}
+ if request_options is not None
+ else {}
+ ),
+ }
+ )
+ )
+ )
+ if _encoded_query_params:
+ ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
+ headers = self._raw_client._client_wrapper.get_headers()
+ if authorization is not None:
+ headers["Authorization"] = str(authorization)
+ if request_options and "additional_headers" in request_options:
+ headers.update(request_options["additional_headers"])
+ try:
+ async with websockets_client_connect(ws_url, extra_headers=headers) as protocol:
+ yield AsyncV2SocketClient(websocket=protocol)
+ except InvalidWebSocketStatus as exc:
+ status_code: int = get_status_code(exc)
+ if status_code == 401:
+ raise ApiError(
+ status_code=status_code,
+ headers=dict(headers),
+ body="Websocket initialized with invalid credentials.",
+ )
+ raise ApiError(
+ status_code=status_code,
+ headers=dict(headers),
+ body="Unexpected error when initializing websocket connection.",
+ )
diff --git a/src/deepgram/speak/v2/raw_client.py b/src/deepgram/speak/v2/raw_client.py
new file mode 100644
index 00000000..4854672f
--- /dev/null
+++ b/src/deepgram/speak/v2/raw_client.py
@@ -0,0 +1,203 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+import urllib.parse
+from contextlib import asynccontextmanager, contextmanager
+
+import websockets.sync.client as websockets_sync_client
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.jsonable_encoder import jsonable_encoder
+from ...core.query_encoder import encode_query
+from ...core.remove_none_from_dict import remove_none_from_dict
+from ...core.request_options import RequestOptions
+from ...core.websocket_compat import InvalidWebSocketStatus, get_status_code
+from ...types.speak_v2encoding import SpeakV2Encoding
+from ...types.speak_v2mip_opt_out import SpeakV2MipOptOut
+from ...types.speak_v2model import SpeakV2Model
+from ...types.speak_v2sample_rate import SpeakV2SampleRate
+from ...types.speak_v2tag import SpeakV2Tag
+from .socket_client import AsyncV2SocketClient, V2SocketClient
+
+try:
+ from websockets.legacy.client import connect as websockets_client_connect # type: ignore
+except ImportError:
+ from websockets import connect as websockets_client_connect # type: ignore
+
+
+class RawV2Client:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ @contextmanager
+ def connect(
+ self,
+ *,
+ model: SpeakV2Model,
+ encoding: typing.Optional[SpeakV2Encoding] = None,
+ sample_rate: typing.Optional[SpeakV2SampleRate] = None,
+ mip_opt_out: typing.Optional[SpeakV2MipOptOut] = None,
+ tag: typing.Optional[SpeakV2Tag] = None,
+ authorization: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> typing.Iterator[V2SocketClient]:
+ """
+ Streaming, turn-based text-to-speech (Flux TTS) built for voice-agent
+ pipelines. Stream LLM tokens in, speak them to the user, and report
+ per-turn billing and timing.
+
+ Parameters
+ ----------
+ model : SpeakV2Model
+
+ encoding : typing.Optional[SpeakV2Encoding]
+
+ sample_rate : typing.Optional[SpeakV2SampleRate]
+
+ mip_opt_out : typing.Optional[SpeakV2MipOptOut]
+
+ tag : typing.Optional[SpeakV2Tag]
+
+ authorization : typing.Optional[str]
+ Use your API key or a [temporary token](/guides/fundamentals/token-based-authentication) for authentication via the `Authorization` header. In client-side environments where custom headers are not supported, use the [`Sec-WebSocket-Protocol`](/guides/deep-dives/using-the-sec-websocket-protocol) header instead.
+
+ **Example:** `Authorization: Token %DEEPGRAM_API_KEY%` or `Authorization: Bearer %DEEPGRAM_TOKEN%`
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ V2SocketClient
+ """
+ ws_url = self._client_wrapper.get_environment().production + "/v2/speak"
+ _encoded_query_params = encode_query(
+ jsonable_encoder(
+ remove_none_from_dict(
+ {
+ "model": model,
+ "encoding": encoding,
+ "sample_rate": sample_rate,
+ "mip_opt_out": mip_opt_out,
+ "tag": tag,
+ **(
+ request_options.get("additional_query_parameters", {}) or {}
+ if request_options is not None
+ else {}
+ ),
+ }
+ )
+ )
+ )
+ if _encoded_query_params:
+ ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
+ headers = self._client_wrapper.get_headers()
+ if authorization is not None:
+ headers["Authorization"] = str(authorization)
+ if request_options and "additional_headers" in request_options:
+ headers.update(request_options["additional_headers"])
+ try:
+ with websockets_sync_client.connect(ws_url, additional_headers=headers) as protocol:
+ yield V2SocketClient(websocket=protocol)
+ except InvalidWebSocketStatus as exc:
+ status_code: int = get_status_code(exc)
+ if status_code == 401:
+ raise ApiError(
+ status_code=status_code,
+ headers=dict(headers),
+ body="Websocket initialized with invalid credentials.",
+ )
+ raise ApiError(
+ status_code=status_code,
+ headers=dict(headers),
+ body="Unexpected error when initializing websocket connection.",
+ )
+
+
+class AsyncRawV2Client:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ @asynccontextmanager
+ async def connect(
+ self,
+ *,
+ model: SpeakV2Model,
+ encoding: typing.Optional[SpeakV2Encoding] = None,
+ sample_rate: typing.Optional[SpeakV2SampleRate] = None,
+ mip_opt_out: typing.Optional[SpeakV2MipOptOut] = None,
+ tag: typing.Optional[SpeakV2Tag] = None,
+ authorization: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> typing.AsyncIterator[AsyncV2SocketClient]:
+ """
+ Streaming, turn-based text-to-speech (Flux TTS) built for voice-agent
+ pipelines. Stream LLM tokens in, speak them to the user, and report
+ per-turn billing and timing.
+
+ Parameters
+ ----------
+ model : SpeakV2Model
+
+ encoding : typing.Optional[SpeakV2Encoding]
+
+ sample_rate : typing.Optional[SpeakV2SampleRate]
+
+ mip_opt_out : typing.Optional[SpeakV2MipOptOut]
+
+ tag : typing.Optional[SpeakV2Tag]
+
+ authorization : typing.Optional[str]
+ Use your API key or a [temporary token](/guides/fundamentals/token-based-authentication) for authentication via the `Authorization` header. In client-side environments where custom headers are not supported, use the [`Sec-WebSocket-Protocol`](/guides/deep-dives/using-the-sec-websocket-protocol) header instead.
+
+ **Example:** `Authorization: Token %DEEPGRAM_API_KEY%` or `Authorization: Bearer %DEEPGRAM_TOKEN%`
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncV2SocketClient
+ """
+ ws_url = self._client_wrapper.get_environment().production + "/v2/speak"
+ _encoded_query_params = encode_query(
+ jsonable_encoder(
+ remove_none_from_dict(
+ {
+ "model": model,
+ "encoding": encoding,
+ "sample_rate": sample_rate,
+ "mip_opt_out": mip_opt_out,
+ "tag": tag,
+ **(
+ request_options.get("additional_query_parameters", {}) or {}
+ if request_options is not None
+ else {}
+ ),
+ }
+ )
+ )
+ )
+ if _encoded_query_params:
+ ws_url = ws_url + "?" + urllib.parse.urlencode(_encoded_query_params)
+ headers = self._client_wrapper.get_headers()
+ if authorization is not None:
+ headers["Authorization"] = str(authorization)
+ if request_options and "additional_headers" in request_options:
+ headers.update(request_options["additional_headers"])
+ try:
+ async with websockets_client_connect(ws_url, extra_headers=headers) as protocol:
+ yield AsyncV2SocketClient(websocket=protocol)
+ except InvalidWebSocketStatus as exc:
+ status_code: int = get_status_code(exc)
+ if status_code == 401:
+ raise ApiError(
+ status_code=status_code,
+ headers=dict(headers),
+ body="Websocket initialized with invalid credentials.",
+ )
+ raise ApiError(
+ status_code=status_code,
+ headers=dict(headers),
+ body="Unexpected error when initializing websocket connection.",
+ )
diff --git a/src/deepgram/speak/v2/requests/__init__.py b/src/deepgram/speak/v2/requests/__init__.py
new file mode 100644
index 00000000..6da073b9
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/__init__.py
@@ -0,0 +1,68 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .speak_v2close import SpeakV2CloseParams
+ from .speak_v2connected import SpeakV2ConnectedParams
+ from .speak_v2error import SpeakV2ErrorParams
+ from .speak_v2flush import SpeakV2FlushParams
+ from .speak_v2flushed import SpeakV2FlushedParams
+ from .speak_v2session_metadata import SpeakV2SessionMetadataParams
+ from .speak_v2speak import SpeakV2SpeakParams
+ from .speak_v2speech_metadata import SpeakV2SpeechMetadataParams
+ from .speak_v2speech_metadata_controls_applied import SpeakV2SpeechMetadataControlsAppliedParams
+ from .speak_v2speech_started import SpeakV2SpeechStartedParams
+ from .speak_v2warning import SpeakV2WarningParams
+_dynamic_imports: typing.Dict[str, str] = {
+ "SpeakV2CloseParams": ".speak_v2close",
+ "SpeakV2ConnectedParams": ".speak_v2connected",
+ "SpeakV2ErrorParams": ".speak_v2error",
+ "SpeakV2FlushParams": ".speak_v2flush",
+ "SpeakV2FlushedParams": ".speak_v2flushed",
+ "SpeakV2SessionMetadataParams": ".speak_v2session_metadata",
+ "SpeakV2SpeakParams": ".speak_v2speak",
+ "SpeakV2SpeechMetadataControlsAppliedParams": ".speak_v2speech_metadata_controls_applied",
+ "SpeakV2SpeechMetadataParams": ".speak_v2speech_metadata",
+ "SpeakV2SpeechStartedParams": ".speak_v2speech_started",
+ "SpeakV2WarningParams": ".speak_v2warning",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "SpeakV2CloseParams",
+ "SpeakV2ConnectedParams",
+ "SpeakV2ErrorParams",
+ "SpeakV2FlushParams",
+ "SpeakV2FlushedParams",
+ "SpeakV2SessionMetadataParams",
+ "SpeakV2SpeakParams",
+ "SpeakV2SpeechMetadataControlsAppliedParams",
+ "SpeakV2SpeechMetadataParams",
+ "SpeakV2SpeechStartedParams",
+ "SpeakV2WarningParams",
+]
diff --git a/src/deepgram/speak/v2/requests/speak_v2close.py b/src/deepgram/speak/v2/requests/speak_v2close.py
new file mode 100644
index 00000000..c6a94f6b
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2close.py
@@ -0,0 +1,12 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+
+
+class SpeakV2CloseParams(typing_extensions.TypedDict):
+ type: typing.Literal["Close"]
+ """
+ Message type identifier
+ """
diff --git a/src/deepgram/speak/v2/requests/speak_v2connected.py b/src/deepgram/speak/v2/requests/speak_v2connected.py
new file mode 100644
index 00000000..a6cc5b01
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2connected.py
@@ -0,0 +1,32 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+
+
+class SpeakV2ConnectedParams(typing_extensions.TypedDict):
+ type: typing.Literal["Connected"]
+ """
+ Message type identifier
+ """
+
+ request_id: str
+ """
+ The unique identifier of the `/v2/speak` request
+ """
+
+ model_name: str
+ """
+ Resolved model name
+ """
+
+ model_version: str
+ """
+ Resolved model version
+ """
+
+ model_uuids: typing.Sequence[str]
+ """
+ Resolved model UUIDs. A list, because a resolved model may be backed by more than one underlying model.
+ """
diff --git a/src/deepgram/speak/v2/requests/speak_v2error.py b/src/deepgram/speak/v2/requests/speak_v2error.py
new file mode 100644
index 00000000..83ff05b1
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2error.py
@@ -0,0 +1,23 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+from ..types.speak_v2error_code import SpeakV2ErrorCode
+
+
+class SpeakV2ErrorParams(typing_extensions.TypedDict):
+ type: typing.Literal["Error"]
+ """
+ Message type identifier
+ """
+
+ code: SpeakV2ErrorCode
+ """
+ A code identifying the error, e.g. `MESSAGE-0000` or `NET-0000`.
+ """
+
+ description: str
+ """
+ Prose description of the error
+ """
diff --git a/src/deepgram/speak/v2/requests/speak_v2flush.py b/src/deepgram/speak/v2/requests/speak_v2flush.py
new file mode 100644
index 00000000..b951d310
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2flush.py
@@ -0,0 +1,12 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+
+
+class SpeakV2FlushParams(typing_extensions.TypedDict):
+ type: typing.Literal["Flush"]
+ """
+ Message type identifier
+ """
diff --git a/src/deepgram/speak/v2/requests/speak_v2flushed.py b/src/deepgram/speak/v2/requests/speak_v2flushed.py
new file mode 100644
index 00000000..9cf1dd81
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2flushed.py
@@ -0,0 +1,17 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+
+
+class SpeakV2FlushedParams(typing_extensions.TypedDict):
+ type: typing.Literal["Flushed"]
+ """
+ Message type identifier
+ """
+
+ speech_id: str
+ """
+ Server-assigned turn identifier
+ """
diff --git a/src/deepgram/speak/v2/requests/speak_v2session_metadata.py b/src/deepgram/speak/v2/requests/speak_v2session_metadata.py
new file mode 100644
index 00000000..9b4b9e03
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2session_metadata.py
@@ -0,0 +1,27 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+
+
+class SpeakV2SessionMetadataParams(typing_extensions.TypedDict):
+ type: typing.Literal["SessionMetadata"]
+ """
+ Message type identifier
+ """
+
+ total_audio_duration_ms: int
+ """
+ Cumulative audio duration produced across the session, in milliseconds
+ """
+
+ total_input_character_count: int
+ """
+ Cumulative raw input character count across the session
+ """
+
+ total_billable_character_count: int
+ """
+ Cumulative billable character count across the session
+ """
diff --git a/src/deepgram/speak/v2/requests/speak_v2speak.py b/src/deepgram/speak/v2/requests/speak_v2speak.py
new file mode 100644
index 00000000..2112ce06
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2speak.py
@@ -0,0 +1,17 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+
+
+class SpeakV2SpeakParams(typing_extensions.TypedDict):
+ type: typing.Literal["Speak"]
+ """
+ Message type identifier
+ """
+
+ text: str
+ """
+ The input text to synthesize
+ """
diff --git a/src/deepgram/speak/v2/requests/speak_v2speech_metadata.py b/src/deepgram/speak/v2/requests/speak_v2speech_metadata.py
new file mode 100644
index 00000000..23b20488
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2speech_metadata.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+from .speak_v2speech_metadata_controls_applied import SpeakV2SpeechMetadataControlsAppliedParams
+
+
+class SpeakV2SpeechMetadataParams(typing_extensions.TypedDict):
+ type: typing.Literal["SpeechMetadata"]
+ """
+ Message type identifier
+ """
+
+ speech_id: str
+ """
+ Server-assigned turn identifier
+ """
+
+ audio_duration_ms: int
+ """
+ Total audio duration produced for this turn, in milliseconds
+ """
+
+ input_character_count: int
+ """
+ Raw input character count for this turn, before text normalization
+ """
+
+ billable_character_count: int
+ """
+ Billable character count for this turn — the input character count with stripped control characters removed. Always less than or equal to `input_character_count`.
+ """
+
+ controls_applied: SpeakV2SpeechMetadataControlsAppliedParams
+ """
+ Controls applied during the turn. Inline pronunciation and pause controls are not available during Early Access, so every count is currently `0`.
+ """
diff --git a/src/deepgram/speak/v2/requests/speak_v2speech_metadata_controls_applied.py b/src/deepgram/speak/v2/requests/speak_v2speech_metadata_controls_applied.py
new file mode 100644
index 00000000..561318ac
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2speech_metadata_controls_applied.py
@@ -0,0 +1,19 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing_extensions
+
+
+class SpeakV2SpeechMetadataControlsAppliedParams(typing_extensions.TypedDict):
+ """
+ Controls applied during the turn. Inline pronunciation and pause controls are not available during Early Access, so every count is currently `0`.
+ """
+
+ pronunciations_applied: int
+ """
+ Pronunciation overrides successfully applied. Mirrors the Aura-2 `dg-pronunciations-applied` REST header. Always `0` during Early Access.
+ """
+
+ pronunciation_warnings: int
+ """
+ Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 `dg-pronunciation-warnings` REST header. Always `0` during Early Access.
+ """
diff --git a/src/deepgram/speak/v2/requests/speak_v2speech_started.py b/src/deepgram/speak/v2/requests/speak_v2speech_started.py
new file mode 100644
index 00000000..ad860b0e
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2speech_started.py
@@ -0,0 +1,17 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+
+
+class SpeakV2SpeechStartedParams(typing_extensions.TypedDict):
+ type: typing.Literal["SpeechStarted"]
+ """
+ Message type identifier
+ """
+
+ speech_id: str
+ """
+ Server-minted identifier for this turn, of the form `dg_sp_<12 hex digits>`. Informational.
+ """
diff --git a/src/deepgram/speak/v2/requests/speak_v2warning.py b/src/deepgram/speak/v2/requests/speak_v2warning.py
new file mode 100644
index 00000000..1cbeb836
--- /dev/null
+++ b/src/deepgram/speak/v2/requests/speak_v2warning.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import typing_extensions
+
+
+class SpeakV2WarningParams(typing_extensions.TypedDict):
+ type: typing.Literal["Warning"]
+ """
+ Message type identifier
+ """
+
+ code: str
+ """
+ Warning code identifying the condition, in `SCREAMING_SNAKE_CASE`. Early Access codes are `NO_ACTIVE_SPEECH` (a speech-scoped message arrived with no active turn) and `SYNTHESIS_RETRYING` (a synthesis request failed and is being retried).
+ """
+
+ description: str
+ """
+ A human-readable description of the warning
+ """
diff --git a/src/deepgram/speak/v2/socket_client.py b/src/deepgram/speak/v2/socket_client.py
new file mode 100644
index 00000000..a2896a45
--- /dev/null
+++ b/src/deepgram/speak/v2/socket_client.py
@@ -0,0 +1,232 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import json
+import logging
+import typing
+
+import websockets.sync.connection as websockets_sync_connection
+from ...core.events import EventEmitterMixin, EventType
+from ...core.unchecked_base_model import construct_type
+from .types.speak_v2close import SpeakV2Close
+from .types.speak_v2connected import SpeakV2Connected
+from .types.speak_v2error import SpeakV2Error
+from .types.speak_v2flush import SpeakV2Flush
+from .types.speak_v2flushed import SpeakV2Flushed
+from .types.speak_v2session_metadata import SpeakV2SessionMetadata
+from .types.speak_v2speak import SpeakV2Speak
+from .types.speak_v2speech_metadata import SpeakV2SpeechMetadata
+from .types.speak_v2speech_started import SpeakV2SpeechStarted
+from .types.speak_v2warning import SpeakV2Warning
+
+try:
+ from websockets.legacy.client import WebSocketClientProtocol # type: ignore
+except ImportError:
+ from websockets import WebSocketClientProtocol # type: ignore
+
+_logger = logging.getLogger(__name__)
+V2SocketClientResponse = typing.Union[
+ bytes,
+ SpeakV2Connected,
+ SpeakV2SpeechStarted,
+ SpeakV2SpeechMetadata,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Warning,
+ SpeakV2Error,
+]
+
+
+class AsyncV2SocketClient(EventEmitterMixin):
+ def __init__(self, *, websocket: WebSocketClientProtocol):
+ super().__init__()
+ self._websocket = websocket
+
+ async def __aiter__(self):
+ async for message in self._websocket:
+ if isinstance(message, bytes):
+ yield message
+ else:
+ try:
+ yield construct_type(type_=V2SocketClientResponse, object_=json.loads(message)) # type: ignore
+ except Exception:
+ _logger.warning(
+ "Skipping unknown WebSocket message; update your SDK version to support new message types."
+ )
+ continue
+
+ async def start_listening(self):
+ """
+ Start listening for messages on the websocket connection.
+
+ Emits events in the following order:
+ - EventType.OPEN when connection is established
+ - EventType.MESSAGE for each message received
+ - EventType.ERROR if an error occurs
+ - EventType.CLOSE when connection is closed
+ """
+ await self._emit_async(EventType.OPEN, None)
+ try:
+ async for raw_message in self._websocket:
+ if isinstance(raw_message, bytes):
+ parsed = raw_message
+ else:
+ json_data = json.loads(raw_message)
+ try:
+ parsed = construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore
+ except Exception:
+ _logger.warning(
+ "Skipping unknown WebSocket message; update your SDK version to support new message types."
+ )
+ continue
+ await self._emit_async(EventType.MESSAGE, parsed)
+ except Exception as exc:
+ await self._emit_async(EventType.ERROR, exc)
+ finally:
+ await self._emit_async(EventType.CLOSE, None)
+
+ async def send_speak(self, message: SpeakV2Speak) -> None:
+ """
+ Send a message to the websocket connection.
+ The message will be sent as a SpeakV2Speak.
+ """
+ await self._send_model(message)
+
+ async def send_flush(self, message: typing.Optional[SpeakV2Flush] = None) -> None:
+ """
+ Send a message to the websocket connection.
+ The message will be sent as a SpeakV2Flush.
+ """
+ await self._send_model(message or SpeakV2Flush(type="Flush"))
+
+ async def send_close(self, message: typing.Optional[SpeakV2Close] = None) -> None:
+ """
+ Send a message to the websocket connection.
+ The message will be sent as a SpeakV2Close.
+ """
+ await self._send_model(message or SpeakV2Close(type="Close"))
+
+ async def recv(self) -> V2SocketClientResponse:
+ """
+ Receive a message from the websocket connection.
+ """
+ data = await self._websocket.recv()
+ if isinstance(data, bytes):
+ return data # type: ignore
+ json_data = json.loads(data)
+ try:
+ return construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore
+ except Exception:
+ _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.")
+ return json_data # type: ignore
+
+ async def _send(self, data: typing.Any) -> None:
+ """
+ Send a message to the websocket connection.
+ """
+ if isinstance(data, dict):
+ data = json.dumps(data)
+ await self._websocket.send(data)
+
+ async def _send_model(self, data: typing.Any) -> None:
+ """
+ Send a Pydantic model to the websocket connection.
+ """
+ await self._send(data.dict())
+
+
+class V2SocketClient(EventEmitterMixin):
+ def __init__(self, *, websocket: websockets_sync_connection.Connection):
+ super().__init__()
+ self._websocket = websocket
+
+ def __iter__(self):
+ for message in self._websocket:
+ if isinstance(message, bytes):
+ yield message
+ else:
+ try:
+ yield construct_type(type_=V2SocketClientResponse, object_=json.loads(message)) # type: ignore
+ except Exception:
+ _logger.warning(
+ "Skipping unknown WebSocket message; update your SDK version to support new message types."
+ )
+ continue
+
+ def start_listening(self):
+ """
+ Start listening for messages on the websocket connection.
+
+ Emits events in the following order:
+ - EventType.OPEN when connection is established
+ - EventType.MESSAGE for each message received
+ - EventType.ERROR if an error occurs
+ - EventType.CLOSE when connection is closed
+ """
+ self._emit(EventType.OPEN, None)
+ try:
+ for raw_message in self._websocket:
+ if isinstance(raw_message, bytes):
+ parsed = raw_message
+ else:
+ json_data = json.loads(raw_message)
+ try:
+ parsed = construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore
+ except Exception:
+ _logger.warning(
+ "Skipping unknown WebSocket message; update your SDK version to support new message types."
+ )
+ continue
+ self._emit(EventType.MESSAGE, parsed)
+ except Exception as exc:
+ self._emit(EventType.ERROR, exc)
+ finally:
+ self._emit(EventType.CLOSE, None)
+
+ def send_speak(self, message: SpeakV2Speak) -> None:
+ """
+ Send a message to the websocket connection.
+ The message will be sent as a SpeakV2Speak.
+ """
+ self._send_model(message)
+
+ def send_flush(self, message: typing.Optional[SpeakV2Flush] = None) -> None:
+ """
+ Send a message to the websocket connection.
+ The message will be sent as a SpeakV2Flush.
+ """
+ self._send_model(message or SpeakV2Flush(type="Flush"))
+
+ def send_close(self, message: typing.Optional[SpeakV2Close] = None) -> None:
+ """
+ Send a message to the websocket connection.
+ The message will be sent as a SpeakV2Close.
+ """
+ self._send_model(message or SpeakV2Close(type="Close"))
+
+ def recv(self) -> V2SocketClientResponse:
+ """
+ Receive a message from the websocket connection.
+ """
+ data = self._websocket.recv()
+ if isinstance(data, bytes):
+ return data # type: ignore
+ json_data = json.loads(data)
+ try:
+ return construct_type(type_=V2SocketClientResponse, object_=json_data) # type: ignore
+ except Exception:
+ _logger.warning("Skipping unknown WebSocket message; update your SDK version to support new message types.")
+ return json_data # type: ignore
+
+ def _send(self, data: typing.Any) -> None:
+ """
+ Send a message to the websocket connection.
+ """
+ if isinstance(data, dict):
+ data = json.dumps(data)
+ self._websocket.send(data)
+
+ def _send_model(self, data: typing.Any) -> None:
+ """
+ Send a Pydantic model to the websocket connection.
+ """
+ self._send(data.dict())
diff --git a/src/deepgram/speak/v2/types/__init__.py b/src/deepgram/speak/v2/types/__init__.py
new file mode 100644
index 00000000..b08407f0
--- /dev/null
+++ b/src/deepgram/speak/v2/types/__init__.py
@@ -0,0 +1,71 @@
+# This file was auto-generated by Fern from our API Definition.
+
+# isort: skip_file
+
+import typing
+from importlib import import_module
+
+if typing.TYPE_CHECKING:
+ from .speak_v2close import SpeakV2Close
+ from .speak_v2connected import SpeakV2Connected
+ from .speak_v2error import SpeakV2Error
+ from .speak_v2error_code import SpeakV2ErrorCode
+ from .speak_v2flush import SpeakV2Flush
+ from .speak_v2flushed import SpeakV2Flushed
+ from .speak_v2session_metadata import SpeakV2SessionMetadata
+ from .speak_v2speak import SpeakV2Speak
+ from .speak_v2speech_metadata import SpeakV2SpeechMetadata
+ from .speak_v2speech_metadata_controls_applied import SpeakV2SpeechMetadataControlsApplied
+ from .speak_v2speech_started import SpeakV2SpeechStarted
+ from .speak_v2warning import SpeakV2Warning
+_dynamic_imports: typing.Dict[str, str] = {
+ "SpeakV2Close": ".speak_v2close",
+ "SpeakV2Connected": ".speak_v2connected",
+ "SpeakV2Error": ".speak_v2error",
+ "SpeakV2ErrorCode": ".speak_v2error_code",
+ "SpeakV2Flush": ".speak_v2flush",
+ "SpeakV2Flushed": ".speak_v2flushed",
+ "SpeakV2SessionMetadata": ".speak_v2session_metadata",
+ "SpeakV2Speak": ".speak_v2speak",
+ "SpeakV2SpeechMetadata": ".speak_v2speech_metadata",
+ "SpeakV2SpeechMetadataControlsApplied": ".speak_v2speech_metadata_controls_applied",
+ "SpeakV2SpeechStarted": ".speak_v2speech_started",
+ "SpeakV2Warning": ".speak_v2warning",
+}
+
+
+def __getattr__(attr_name: str) -> typing.Any:
+ module_name = _dynamic_imports.get(attr_name)
+ if module_name is None:
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
+ try:
+ module = import_module(module_name, __package__)
+ if module_name == f".{attr_name}":
+ return module
+ else:
+ return getattr(module, attr_name)
+ except ImportError as e:
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
+ except AttributeError as e:
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
+
+
+def __dir__():
+ lazy_attrs = list(_dynamic_imports.keys())
+ return sorted(lazy_attrs)
+
+
+__all__ = [
+ "SpeakV2Close",
+ "SpeakV2Connected",
+ "SpeakV2Error",
+ "SpeakV2ErrorCode",
+ "SpeakV2Flush",
+ "SpeakV2Flushed",
+ "SpeakV2SessionMetadata",
+ "SpeakV2Speak",
+ "SpeakV2SpeechMetadata",
+ "SpeakV2SpeechMetadataControlsApplied",
+ "SpeakV2SpeechStarted",
+ "SpeakV2Warning",
+]
diff --git a/src/deepgram/speak/v2/types/speak_v2close.py b/src/deepgram/speak/v2/types/speak_v2close.py
new file mode 100644
index 00000000..3afd1934
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2close.py
@@ -0,0 +1,23 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class SpeakV2Close(UncheckedBaseModel):
+ type: typing.Literal["Close"] = pydantic.Field(default="Close")
+ """
+ Message type identifier
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/speak/v2/types/speak_v2connected.py b/src/deepgram/speak/v2/types/speak_v2connected.py
new file mode 100644
index 00000000..197c5792
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2connected.py
@@ -0,0 +1,43 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class SpeakV2Connected(UncheckedBaseModel):
+ type: typing.Literal["Connected"] = pydantic.Field(default="Connected")
+ """
+ Message type identifier
+ """
+
+ request_id: str = pydantic.Field()
+ """
+ The unique identifier of the `/v2/speak` request
+ """
+
+ model_name: str = pydantic.Field()
+ """
+ Resolved model name
+ """
+
+ model_version: str = pydantic.Field()
+ """
+ Resolved model version
+ """
+
+ model_uuids: typing.List[str] = pydantic.Field()
+ """
+ Resolved model UUIDs. A list, because a resolved model may be backed by more than one underlying model.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/speak/v2/types/speak_v2error.py b/src/deepgram/speak/v2/types/speak_v2error.py
new file mode 100644
index 00000000..9bb02c21
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2error.py
@@ -0,0 +1,34 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .speak_v2error_code import SpeakV2ErrorCode
+
+
+class SpeakV2Error(UncheckedBaseModel):
+ type: typing.Literal["Error"] = pydantic.Field(default="Error")
+ """
+ Message type identifier
+ """
+
+ code: SpeakV2ErrorCode = pydantic.Field()
+ """
+ A code identifying the error, e.g. `MESSAGE-0000` or `NET-0000`.
+ """
+
+ description: str = pydantic.Field()
+ """
+ Prose description of the error
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/speak/v2/types/speak_v2error_code.py b/src/deepgram/speak/v2/types/speak_v2error_code.py
new file mode 100644
index 00000000..8d9e1df1
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2error_code.py
@@ -0,0 +1,8 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+SpeakV2ErrorCode = typing.Union[
+ typing.Literal["MESSAGE-0000", "DATA-0000", "BIG-0000", "NET-0000", "NET-0001", "NET-0002", "NET-0003", "NET-0004"],
+ typing.Any,
+]
diff --git a/src/deepgram/speak/v2/types/speak_v2flush.py b/src/deepgram/speak/v2/types/speak_v2flush.py
new file mode 100644
index 00000000..088df676
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2flush.py
@@ -0,0 +1,23 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class SpeakV2Flush(UncheckedBaseModel):
+ type: typing.Literal["Flush"] = pydantic.Field(default="Flush")
+ """
+ Message type identifier
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/speak/v2/types/speak_v2flushed.py b/src/deepgram/speak/v2/types/speak_v2flushed.py
new file mode 100644
index 00000000..4739cf47
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2flushed.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class SpeakV2Flushed(UncheckedBaseModel):
+ type: typing.Literal["Flushed"] = pydantic.Field(default="Flushed")
+ """
+ Message type identifier
+ """
+
+ speech_id: str = pydantic.Field()
+ """
+ Server-assigned turn identifier
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/speak/v2/types/speak_v2session_metadata.py b/src/deepgram/speak/v2/types/speak_v2session_metadata.py
new file mode 100644
index 00000000..cf96e019
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2session_metadata.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class SpeakV2SessionMetadata(UncheckedBaseModel):
+ type: typing.Literal["SessionMetadata"] = pydantic.Field(default="SessionMetadata")
+ """
+ Message type identifier
+ """
+
+ total_audio_duration_ms: int = pydantic.Field()
+ """
+ Cumulative audio duration produced across the session, in milliseconds
+ """
+
+ total_input_character_count: int = pydantic.Field()
+ """
+ Cumulative raw input character count across the session
+ """
+
+ total_billable_character_count: int = pydantic.Field()
+ """
+ Cumulative billable character count across the session
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/speak/v2/types/speak_v2speak.py b/src/deepgram/speak/v2/types/speak_v2speak.py
new file mode 100644
index 00000000..6d327781
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2speak.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class SpeakV2Speak(UncheckedBaseModel):
+ type: typing.Literal["Speak"] = pydantic.Field(default="Speak")
+ """
+ Message type identifier
+ """
+
+ text: str = pydantic.Field()
+ """
+ The input text to synthesize
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/speak/v2/types/speak_v2speech_metadata.py b/src/deepgram/speak/v2/types/speak_v2speech_metadata.py
new file mode 100644
index 00000000..9df85f0e
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2speech_metadata.py
@@ -0,0 +1,49 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+from .speak_v2speech_metadata_controls_applied import SpeakV2SpeechMetadataControlsApplied
+
+
+class SpeakV2SpeechMetadata(UncheckedBaseModel):
+ type: typing.Literal["SpeechMetadata"] = pydantic.Field(default="SpeechMetadata")
+ """
+ Message type identifier
+ """
+
+ speech_id: str = pydantic.Field()
+ """
+ Server-assigned turn identifier
+ """
+
+ audio_duration_ms: int = pydantic.Field()
+ """
+ Total audio duration produced for this turn, in milliseconds
+ """
+
+ input_character_count: int = pydantic.Field()
+ """
+ Raw input character count for this turn, before text normalization
+ """
+
+ billable_character_count: int = pydantic.Field()
+ """
+ Billable character count for this turn — the input character count with stripped control characters removed. Always less than or equal to `input_character_count`.
+ """
+
+ controls_applied: SpeakV2SpeechMetadataControlsApplied = pydantic.Field()
+ """
+ Controls applied during the turn. Inline pronunciation and pause controls are not available during Early Access, so every count is currently `0`.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/speak/v2/types/speak_v2speech_metadata_controls_applied.py b/src/deepgram/speak/v2/types/speak_v2speech_metadata_controls_applied.py
new file mode 100644
index 00000000..e24d03cd
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2speech_metadata_controls_applied.py
@@ -0,0 +1,32 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class SpeakV2SpeechMetadataControlsApplied(UncheckedBaseModel):
+ """
+ Controls applied during the turn. Inline pronunciation and pause controls are not available during Early Access, so every count is currently `0`.
+ """
+
+ pronunciations_applied: int = pydantic.Field()
+ """
+ Pronunciation overrides successfully applied. Mirrors the Aura-2 `dg-pronunciations-applied` REST header. Always `0` during Early Access.
+ """
+
+ pronunciation_warnings: int = pydantic.Field()
+ """
+ Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 `dg-pronunciation-warnings` REST header. Always `0` during Early Access.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/speak/v2/types/speak_v2speech_started.py b/src/deepgram/speak/v2/types/speak_v2speech_started.py
new file mode 100644
index 00000000..5dcd35bc
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2speech_started.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class SpeakV2SpeechStarted(UncheckedBaseModel):
+ type: typing.Literal["SpeechStarted"] = pydantic.Field(default="SpeechStarted")
+ """
+ Message type identifier
+ """
+
+ speech_id: str = pydantic.Field()
+ """
+ Server-minted identifier for this turn, of the form `dg_sp_<12 hex digits>`. Informational.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/speak/v2/types/speak_v2warning.py b/src/deepgram/speak/v2/types/speak_v2warning.py
new file mode 100644
index 00000000..967b2279
--- /dev/null
+++ b/src/deepgram/speak/v2/types/speak_v2warning.py
@@ -0,0 +1,33 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ....core.pydantic_utilities import IS_PYDANTIC_V2
+from ....core.unchecked_base_model import UncheckedBaseModel
+
+
+class SpeakV2Warning(UncheckedBaseModel):
+ type: typing.Literal["Warning"] = pydantic.Field(default="Warning")
+ """
+ Message type identifier
+ """
+
+ code: str = pydantic.Field()
+ """
+ Warning code identifying the condition, in `SCREAMING_SNAKE_CASE`. Early Access codes are `NO_ACTIVE_SPEECH` (a speech-scoped message arrived with no active turn) and `SYNTHESIS_RETRYING` (a synthesis request failed and is being retried).
+ """
+
+ description: str = pydantic.Field()
+ """
+ A human-readable description of the warning
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/deepgram/types/__init__.py b/src/deepgram/types/__init__.py
index f927e513..eda099b1 100644
--- a/src/deepgram/types/__init__.py
+++ b/src/deepgram/types/__init__.py
@@ -257,6 +257,11 @@
from .speak_v1response import SpeakV1Response
from .speak_v1sample_rate import SpeakV1SampleRate
from .speak_v1speed import SpeakV1Speed
+ from .speak_v2encoding import SpeakV2Encoding
+ from .speak_v2mip_opt_out import SpeakV2MipOptOut
+ from .speak_v2model import SpeakV2Model
+ from .speak_v2sample_rate import SpeakV2SampleRate
+ from .speak_v2tag import SpeakV2Tag
from .think_settings_v1 import ThinkSettingsV1
from .think_settings_v1context_length import ThinkSettingsV1ContextLength
from .think_settings_v1endpoint import ThinkSettingsV1Endpoint
@@ -496,6 +501,11 @@
"SpeakV1Response": ".speak_v1response",
"SpeakV1SampleRate": ".speak_v1sample_rate",
"SpeakV1Speed": ".speak_v1speed",
+ "SpeakV2Encoding": ".speak_v2encoding",
+ "SpeakV2MipOptOut": ".speak_v2mip_opt_out",
+ "SpeakV2Model": ".speak_v2model",
+ "SpeakV2SampleRate": ".speak_v2sample_rate",
+ "SpeakV2Tag": ".speak_v2tag",
"ThinkSettingsV1": ".think_settings_v1",
"ThinkSettingsV1ContextLength": ".think_settings_v1context_length",
"ThinkSettingsV1Endpoint": ".think_settings_v1endpoint",
@@ -757,6 +767,11 @@ def __dir__():
"SpeakV1Response",
"SpeakV1SampleRate",
"SpeakV1Speed",
+ "SpeakV2Encoding",
+ "SpeakV2MipOptOut",
+ "SpeakV2Model",
+ "SpeakV2SampleRate",
+ "SpeakV2Tag",
"ThinkSettingsV1",
"ThinkSettingsV1ContextLength",
"ThinkSettingsV1Endpoint",
diff --git a/src/deepgram/types/deepgram_listen_provider_v2.py b/src/deepgram/types/deepgram_listen_provider_v2.py
index 7c29c6f1..e55bf684 100644
--- a/src/deepgram/types/deepgram_listen_provider_v2.py
+++ b/src/deepgram/types/deepgram_listen_provider_v2.py
@@ -35,6 +35,21 @@ class DeepgramListenProviderV2(UncheckedBaseModel):
`exclude=True` keeps it off the wire (the API rejects unknown fields).
"""
+ eot_threshold: typing.Optional[float] = pydantic.Field(default=None)
+ """
+ End-of-turn confidence required to finish a turn. Valid range: 0.5 - 0.9. Defaults to 0.7.
+ """
+
+ eager_eot_threshold: typing.Optional[float] = pydantic.Field(default=None)
+ """
+ End-of-turn confidence required to fire an eager end-of-turn event. When set, enables EagerEndOfTurn and TurnResumed events. Valid range: 0.3 - 0.9.
+ """
+
+ eot_timeout_ms: typing.Optional[int] = pydantic.Field(default=None)
+ """
+ A turn will be finished when this much time in milliseconds has passed after speech, regardless of EOT confidence. Defaults to 5000.
+ """
+
keyterms: typing.Optional[typing.List[str]] = pydantic.Field(default=None)
"""
Prompt keyterm recognition to improve Keyword Recall Rate
diff --git a/src/deepgram/types/speak_v2encoding.py b/src/deepgram/types/speak_v2encoding.py
new file mode 100644
index 00000000..3447211f
--- /dev/null
+++ b/src/deepgram/types/speak_v2encoding.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+SpeakV2Encoding = typing.Union[typing.Literal["linear16", "mulaw", "alaw"], typing.Any]
diff --git a/src/deepgram/types/speak_v2mip_opt_out.py b/src/deepgram/types/speak_v2mip_opt_out.py
new file mode 100644
index 00000000..6165e627
--- /dev/null
+++ b/src/deepgram/types/speak_v2mip_opt_out.py
@@ -0,0 +1,8 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+SpeakV2MipOptOut = typing.Any
+"""
+Opts out requests from the Deepgram Model Improvement Program. Refer to our Docs for pricing impacts before setting this to true. https://dpgr.am/deepgram-mip
+"""
diff --git a/src/deepgram/types/speak_v2model.py b/src/deepgram/types/speak_v2model.py
new file mode 100644
index 00000000..98ba0ea9
--- /dev/null
+++ b/src/deepgram/types/speak_v2model.py
@@ -0,0 +1,6 @@
+# This file was auto-generated by Fern from our API Definition.
+
+SpeakV2Model = str
+"""
+The Flux TTS model used to synthesize speech. Required on every connection. Model strings follow the format `flux-{voice}-{language}` (e.g. `flux-alexis-en`). An Aura model string is rejected on `/v2/speak`; use `/v1/speak` for Aura voices.
+"""
diff --git a/src/deepgram/types/speak_v2sample_rate.py b/src/deepgram/types/speak_v2sample_rate.py
new file mode 100644
index 00000000..988e6af0
--- /dev/null
+++ b/src/deepgram/types/speak_v2sample_rate.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+SpeakV2SampleRate = typing.Union[typing.Literal["8000", "16000", "24000", "32000", "44100", "48000"], typing.Any]
diff --git a/src/deepgram/types/speak_v2tag.py b/src/deepgram/types/speak_v2tag.py
new file mode 100644
index 00000000..235bcc73
--- /dev/null
+++ b/src/deepgram/types/speak_v2tag.py
@@ -0,0 +1,9 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+SpeakV2Tag = typing.Any
+"""
+Label your requests for the purpose of identification during usage
+reporting. Repeatable.
+"""
diff --git a/tests/custom/test_agent_update_listen.py b/tests/custom/test_agent_update_listen.py
new file mode 100644
index 00000000..667f7623
--- /dev/null
+++ b/tests/custom/test_agent_update_listen.py
@@ -0,0 +1,55 @@
+"""
+Coverage for the Voice Agent mid-session listen-reconfigure surface added in the
+2026-07-08 regen:
+
+ * ``send_update_listen(AgentV1UpdateListen(...))`` — client-sent control message
+ that serializes to ``{"type": "UpdateListen", "listen": {"provider": {...}}}``.
+ * ``AgentV1ListenUpdated`` — the server acknowledgement, which must resolve out
+ of the agent socket response union via its ``type`` discriminant.
+
+Driven with a fake websocket / construct_type — no network.
+"""
+
+import json
+
+from deepgram.agent.v1.socket_client import V1SocketClient, V1SocketClientResponse
+from deepgram.agent.v1.types.agent_v1listen_updated import AgentV1ListenUpdated
+from deepgram.agent.v1.types.agent_v1update_listen import AgentV1UpdateListen
+from deepgram.agent.v1.types.agent_v1update_listen_listen import AgentV1UpdateListenListen
+from deepgram.core.unchecked_base_model import construct_type
+from deepgram.types.deepgram_listen_provider_v2 import DeepgramListenProviderV2
+
+
+class _FakeWebSocket:
+ def __init__(self):
+ self.sent = []
+
+ def send(self, data):
+ self.sent.append(data)
+
+
+def _sent_json(ws):
+ assert len(ws.sent) == 1
+ payload = ws.sent[0]
+ return json.loads(payload) if isinstance(payload, str) else payload
+
+
+class TestSendUpdateListen:
+ def test_serializes_provider_on_the_wire(self):
+ ws = _FakeWebSocket()
+ message = AgentV1UpdateListen(
+ listen=AgentV1UpdateListenListen(provider=DeepgramListenProviderV2(model="flux-general-en"))
+ )
+ V1SocketClient(websocket=ws).send_update_listen(message)
+
+ sent = _sent_json(ws)
+ assert sent["type"] == "UpdateListen"
+ assert sent["listen"]["provider"]["type"] == "deepgram"
+ assert sent["listen"]["provider"]["model"] == "flux-general-en"
+
+
+class TestListenUpdatedResponse:
+ def test_resolves_from_response_union(self):
+ obj = construct_type(type_=V1SocketClientResponse, object_={"type": "ListenUpdated"})
+ assert isinstance(obj, AgentV1ListenUpdated)
+ assert obj.type == "ListenUpdated"
diff --git a/tests/custom/test_eot_thresholds_feature.py b/tests/custom/test_eot_thresholds_feature.py
new file mode 100644
index 00000000..7a2c1aa3
--- /dev/null
+++ b/tests/custom/test_eot_thresholds_feature.py
@@ -0,0 +1,70 @@
+"""
+Forward-feature coverage for the Flux end-of-turn tuning fields added in the
+2026-07-08 regen: ``eot_threshold``, ``eager_eot_threshold`` (floats) and
+``eot_timeout_ms`` (int). These are real wire fields and MUST serialize.
+
+Covered on all three listen-provider models that carry them — the top-level
+``DeepgramListenProviderV2`` plus the two Voice Agent listen-provider variants —
+with kwargs and dict, asserting the values land on the wire and are omitted when
+absent. The frozen ``language_hint`` back-compat shim on these same models must
+not interfere (see test_language_hint_compat.py).
+"""
+
+from deepgram.agent.v1.types.agent_v1settings_agent_context_listen_provider import (
+ AgentV1SettingsAgentContextListenProvider_V2,
+)
+from deepgram.agent.v1.types.agent_v1settings_agent_listen_provider import (
+ AgentV1SettingsAgentListenProvider_V2,
+)
+from deepgram.core.pydantic_utilities import IS_PYDANTIC_V2
+from deepgram.types.deepgram_listen_provider_v2 import DeepgramListenProviderV2
+
+
+def _from_dict(cls, payload):
+ return cls.model_validate(payload) if IS_PYDANTIC_V2 else cls.parse_obj(payload)
+
+
+class TestEotThresholdsProvider:
+ """DeepgramListenProviderV2 — the top-level Flux STT provider."""
+
+ def test_all_three_serialize(self):
+ dumped = DeepgramListenProviderV2(
+ model="flux-general-en",
+ eot_threshold=0.7,
+ eager_eot_threshold=0.4,
+ eot_timeout_ms=5000,
+ ).dict()
+ assert dumped["eot_threshold"] == 0.7
+ assert dumped["eager_eot_threshold"] == 0.4
+ assert dumped["eot_timeout_ms"] == 5000
+
+ def test_from_dict(self):
+ provider = _from_dict(
+ DeepgramListenProviderV2,
+ {"model": "flux-general-en", "eot_threshold": 0.65, "eot_timeout_ms": 4000},
+ )
+ assert provider.eot_threshold == 0.65
+ assert provider.eot_timeout_ms == 4000
+
+ def test_omitted_when_absent(self):
+ dumped = DeepgramListenProviderV2(model="flux-general-en").dict()
+ assert dumped.get("eot_threshold") is None
+ assert dumped.get("eager_eot_threshold") is None
+ assert dumped.get("eot_timeout_ms") is None
+
+
+class TestEotThresholdsAgentProviders:
+ """The two Voice Agent listen-provider V2 variants carry the same fields."""
+
+ def test_context_listen_provider_serializes(self):
+ dumped = AgentV1SettingsAgentContextListenProvider_V2(
+ model="flux-general-en", eot_threshold=0.8, eot_timeout_ms=6000
+ ).dict()
+ assert dumped["eot_threshold"] == 0.8
+ assert dumped["eot_timeout_ms"] == 6000
+
+ def test_agent_listen_provider_serializes(self):
+ dumped = AgentV1SettingsAgentListenProvider_V2(
+ model="flux-general-en", eager_eot_threshold=0.35
+ ).dict()
+ assert dumped["eager_eot_threshold"] == 0.35
diff --git a/tests/custom/test_socket_client_shims.py b/tests/custom/test_socket_client_shims.py
index 7670e9f3..22aba5a7 100644
--- a/tests/custom/test_socket_client_shims.py
+++ b/tests/custom/test_socket_client_shims.py
@@ -21,6 +21,7 @@
from deepgram.agent.v1.socket_client import V1SocketClient, _sanitize_numeric_types
from deepgram.listen.v2.socket_client import V2SocketClient
from deepgram.listen.v2.types.listen_v2close_stream import ListenV2CloseStream
+from deepgram.speak.v2.socket_client import V2SocketClient as SpeakV2SocketClient
class _FakeWebSocket:
@@ -86,6 +87,16 @@ def test_agent_keep_alive_no_arg(self):
V1SocketClient(websocket=ws).send_keep_alive()
assert _sent_json(ws)["type"] == "KeepAlive"
+ def test_speak_v2_flush_no_arg(self):
+ ws = _FakeWebSocket()
+ SpeakV2SocketClient(websocket=ws).send_flush()
+ assert _sent_json(ws)["type"] == "Flush"
+
+ def test_speak_v2_close_no_arg(self):
+ ws = _FakeWebSocket()
+ SpeakV2SocketClient(websocket=ws).send_close()
+ assert _sent_json(ws)["type"] == "Close"
+
class TestSendConfigureRawShim:
def test_passthrough_dict_is_sent_verbatim(self):
diff --git a/tests/custom/test_speak_v2_socket.py b/tests/custom/test_speak_v2_socket.py
new file mode 100644
index 00000000..fb5dac04
--- /dev/null
+++ b/tests/custom/test_speak_v2_socket.py
@@ -0,0 +1,123 @@
+"""
+Coverage for the Speak V2 (Flux TTS) WebSocket socket client, added in the
+2026-07-08 regen. Speak V2 is a public product but shipped with only indirect
+coverage; this pins the behavior a future regen must not silently change:
+
+ * send serialization — ``send_speak`` puts ``text`` on the wire; the no-payload
+ control sends ``send_flush()`` / ``send_close()`` are callable with no
+ argument and emit the correct default control message (hand-applied shim,
+ frozen in .fernignore).
+ * response parsing — inbound JSON resolves to the right ``SpeakV2*`` type via
+ the ``type`` discriminant, and binary frames pass through as ``bytes``.
+ * broad ``except`` — the listen loop surfaces *any* exception as an ERROR event
+ (the generator narrows to ``websockets.WebSocketException``; the patch keeps
+ the custom-transport error contract, which can raise arbitrary types).
+
+Driven with a fake websocket — no network.
+"""
+
+import json
+
+from deepgram.core.events import EventType
+from deepgram.speak.v2.socket_client import V2SocketClient
+from deepgram.speak.v2.types.speak_v2connected import SpeakV2Connected
+from deepgram.speak.v2.types.speak_v2error import SpeakV2Error
+from deepgram.speak.v2.types.speak_v2flushed import SpeakV2Flushed
+from deepgram.speak.v2.types.speak_v2speak import SpeakV2Speak
+
+
+class _FakeWebSocket:
+ """Captures ``.send()`` payloads and replays ``incoming`` on iteration / recv."""
+
+ def __init__(self, incoming=None, raise_on_iter=None):
+ self.sent = []
+ self._incoming = list(incoming or [])
+ self._raise_on_iter = raise_on_iter
+
+ def send(self, data):
+ self.sent.append(data)
+
+ def recv(self):
+ return self._incoming.pop(0)
+
+ def __iter__(self):
+ if self._raise_on_iter is not None:
+ raise self._raise_on_iter
+ yield from self._incoming
+
+
+def _sent_json(ws):
+ assert len(ws.sent) == 1
+ payload = ws.sent[0]
+ return json.loads(payload) if isinstance(payload, str) else payload
+
+
+class TestSpeakV2Send:
+ def test_send_speak_serializes_text(self):
+ ws = _FakeWebSocket()
+ V2SocketClient(websocket=ws).send_speak(SpeakV2Speak(text="Hello, world!"))
+ assert _sent_json(ws) == {"type": "Speak", "text": "Hello, world!"}
+
+ def test_send_flush_no_arg_emits_default(self):
+ ws = _FakeWebSocket()
+ V2SocketClient(websocket=ws).send_flush()
+ assert _sent_json(ws)["type"] == "Flush"
+
+ def test_send_close_no_arg_emits_default(self):
+ ws = _FakeWebSocket()
+ V2SocketClient(websocket=ws).send_close()
+ assert _sent_json(ws)["type"] == "Close"
+
+
+class TestSpeakV2ResponseParsing:
+ def test_recv_parses_connected(self):
+ ws = _FakeWebSocket(
+ incoming=[
+ json.dumps(
+ {
+ "type": "Connected",
+ "request_id": "req-1",
+ "model_name": "flux-alexis-en",
+ "model_version": "1",
+ "model_uuids": ["uuid-1"],
+ }
+ )
+ ]
+ )
+ msg = V2SocketClient(websocket=ws).recv()
+ assert isinstance(msg, SpeakV2Connected)
+ assert msg.request_id == "req-1"
+
+ def test_recv_parses_flushed_and_error(self):
+ ws = _FakeWebSocket(
+ incoming=[
+ json.dumps({"type": "Flushed", "speech_id": "s-1"}),
+ json.dumps({"type": "Error", "code": "SOME_CODE", "description": "boom"}),
+ ]
+ )
+ client = V2SocketClient(websocket=ws)
+ assert isinstance(client.recv(), SpeakV2Flushed)
+ assert isinstance(client.recv(), SpeakV2Error)
+
+ def test_recv_returns_binary_audio_passthrough(self):
+ audio = b"\x00\x01\x02audio-frame"
+ ws = _FakeWebSocket(incoming=[audio])
+ assert V2SocketClient(websocket=ws).recv() == audio
+
+
+class TestSpeakV2Broadexcept:
+ def test_listen_loop_surfaces_non_websocket_error(self):
+ # A custom transport can raise arbitrary exception types from iteration;
+ # the narrow generated catch would let this escape instead of emitting ERROR.
+ boom = ValueError("transport blew up")
+ ws = _FakeWebSocket(raise_on_iter=boom)
+ client = V2SocketClient(websocket=ws)
+
+ events = []
+ client.on(EventType.ERROR, lambda exc: events.append(("error", exc)))
+ client.on(EventType.CLOSE, lambda _: events.append(("close", None)))
+
+ client.start_listening()
+
+ assert ("error", boom) in events
+ assert events[-1] == ("close", None)
diff --git a/tests/manual/speak/v2/connect/async.py b/tests/manual/speak/v2/connect/async.py
new file mode 100644
index 00000000..f1e960ff
--- /dev/null
+++ b/tests/manual/speak/v2/connect/async.py
@@ -0,0 +1,107 @@
+import asyncio
+
+from dotenv import load_dotenv
+
+print("Starting async speak v2 connect example script")
+load_dotenv()
+print("Environment variables loaded")
+
+from typing import Union
+
+from deepgram import AsyncDeepgramClient
+from deepgram.core.events import EventType
+from deepgram.speak.v2.types import (
+ SpeakV2Connected,
+ SpeakV2Error,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Speak,
+ SpeakV2SpeechMetadata,
+ SpeakV2SpeechStarted,
+ SpeakV2Warning,
+)
+
+SpeakV2SocketClientResponse = Union[
+ bytes,
+ SpeakV2Connected,
+ SpeakV2SpeechStarted,
+ SpeakV2SpeechMetadata,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Warning,
+ SpeakV2Error,
+]
+
+print("Initializing AsyncDeepgramClient")
+client = AsyncDeepgramClient()
+print("AsyncDeepgramClient initialized")
+
+
+async def main() -> None:
+ try:
+ model = "flux-alexis-en"
+ encoding = "linear16"
+ sample_rate = "24000"
+ print(f"Establishing async connection - Model: {model}, Encoding: {encoding}, Sample Rate: {sample_rate}")
+ async with client.speak.v2.connect(model=model, encoding=encoding, sample_rate=sample_rate) as connection:
+ print("Connection context entered")
+
+ def on_message(message: SpeakV2SocketClientResponse) -> None:
+ if isinstance(message, bytes):
+ print("Received audio event")
+ print(f"Event body (audio data length): {len(message)} bytes")
+ else:
+ msg_type = getattr(message, "type", "Unknown")
+ print(f"Received {msg_type} event")
+ print(f"Event body: {message}")
+
+ print("Registering event handlers")
+ connection.on(EventType.OPEN, lambda _: print("Connection opened"))
+ connection.on(EventType.MESSAGE, on_message)
+ connection.on(EventType.CLOSE, lambda _: print("Connection closed"))
+ connection.on(EventType.ERROR, lambda error: print(f"Connection error: {error}"))
+ print("Event handlers registered")
+
+ # EXAMPLE ONLY: Start listening task and cancel after brief demo
+ # In production, you would typically await connection.start_listening() directly
+ # which runs until the connection closes or is interrupted
+ print("Starting listening task")
+ listen_task = asyncio.create_task(connection.start_listening())
+
+ # Send text to be converted to speech
+ print("Sending Speak message")
+ await connection.send_speak(SpeakV2Speak(text="Hello, world!"))
+
+ # Send control messages
+ print("Sending Flush control message")
+ await connection.send_flush()
+ print("Sending Close control message")
+ await connection.send_close()
+
+ print("Waiting 3 seconds for events...")
+ await asyncio.sleep(3) # EXAMPLE ONLY: Wait briefly to see some events before exiting
+ print("Cancelling listening task")
+ listen_task.cancel()
+ print("Exiting connection context")
+ except Exception as e:
+ print(f"Error occurred: {type(e).__name__}")
+ # Log request headers if available
+ if hasattr(e, "request_headers"):
+ print(f"Request headers: {e.request_headers}")
+ elif hasattr(e, "request") and hasattr(e.request, "headers"):
+ print(f"Request headers: {e.request.headers}")
+ # Log response headers if available
+ if hasattr(e, "headers"):
+ print(f"Response headers: {e.headers}")
+ # Log status code if available
+ if hasattr(e, "status_code"):
+ print(f"Status code: {e.status_code}")
+ # Log body if available
+ if hasattr(e, "body"):
+ print(f"Response body: {e.body}")
+ print(f"Caught: {e}")
+
+
+print("Running async main function")
+asyncio.run(main())
+print("Script completed")
diff --git a/tests/manual/speak/v2/connect/main.py b/tests/manual/speak/v2/connect/main.py
new file mode 100644
index 00000000..9f3940d3
--- /dev/null
+++ b/tests/manual/speak/v2/connect/main.py
@@ -0,0 +1,99 @@
+from dotenv import load_dotenv
+
+print("Starting speak v2 connect example script")
+load_dotenv()
+print("Environment variables loaded")
+
+import threading
+import time
+from typing import Union
+
+from deepgram import DeepgramClient
+from deepgram.core.events import EventType
+from deepgram.speak.v2.types import (
+ SpeakV2Connected,
+ SpeakV2Error,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Speak,
+ SpeakV2SpeechMetadata,
+ SpeakV2SpeechStarted,
+ SpeakV2Warning,
+)
+
+SpeakV2SocketClientResponse = Union[
+ bytes,
+ SpeakV2Connected,
+ SpeakV2SpeechStarted,
+ SpeakV2SpeechMetadata,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Warning,
+ SpeakV2Error,
+]
+
+print("Initializing DeepgramClient")
+client = DeepgramClient()
+print("DeepgramClient initialized")
+
+try:
+ model = "flux-alexis-en"
+ encoding = "linear16"
+ sample_rate = "24000"
+ print(f"Establishing connection - Model: {model}, Encoding: {encoding}, Sample Rate: {sample_rate}")
+ with client.speak.v2.connect(model=model, encoding=encoding, sample_rate=sample_rate) as connection:
+ print("Connection context entered")
+
+ def on_message(message: SpeakV2SocketClientResponse) -> None:
+ if isinstance(message, bytes):
+ print("Received audio event")
+ print(f"Event body (audio data length): {len(message)} bytes")
+ else:
+ msg_type = getattr(message, "type", "Unknown")
+ print(f"Received {msg_type} event")
+ print(f"Event body: {message}")
+
+ print("Registering event handlers")
+ connection.on(EventType.OPEN, lambda _: print("Connection opened"))
+ connection.on(EventType.MESSAGE, on_message)
+ connection.on(EventType.CLOSE, lambda _: print("Connection closed"))
+ connection.on(EventType.ERROR, lambda error: print(f"Connection error: {error}"))
+ print("Event handlers registered")
+
+ # EXAMPLE ONLY: Start listening in a background thread for demo purposes
+ # In production, you would typically call connection.start_listening() directly
+ # which blocks until the connection closes, or integrate into your async event loop
+ print("Starting listening thread")
+ threading.Thread(target=connection.start_listening, daemon=True).start()
+
+ # Send text to be converted to speech
+ print("Sending Speak message")
+ connection.send_speak(SpeakV2Speak(text="Hello, world!"))
+
+ # Send control messages
+ print("Sending Flush control message")
+ connection.send_flush()
+ print("Sending Close control message")
+ connection.send_close()
+
+ print("Waiting 3 seconds for events...")
+ time.sleep(3) # EXAMPLE ONLY: Wait briefly to see some events before exiting
+ print("Exiting connection context")
+except Exception as e:
+ print(f"Error occurred: {type(e).__name__}")
+ # Log request headers if available
+ if hasattr(e, "request_headers"):
+ print(f"Request headers: {e.request_headers}")
+ elif hasattr(e, "request") and hasattr(e.request, "headers"):
+ print(f"Request headers: {e.request.headers}")
+ # Log response headers if available
+ if hasattr(e, "headers"):
+ print(f"Response headers: {e.headers}")
+ # Log status code if available
+ if hasattr(e, "status_code"):
+ print(f"Status code: {e.status_code}")
+ # Log body if available
+ if hasattr(e, "body"):
+ print(f"Response body: {e.body}")
+ print(f"Caught: {e}")
+print("Script completed")
diff --git a/tests/manual/speak/v2/connect/with_auth_token.py b/tests/manual/speak/v2/connect/with_auth_token.py
new file mode 100644
index 00000000..ca0d2960
--- /dev/null
+++ b/tests/manual/speak/v2/connect/with_auth_token.py
@@ -0,0 +1,109 @@
+from dotenv import load_dotenv
+
+print("Starting speak v2 connect with auth token example script")
+load_dotenv()
+print("Environment variables loaded")
+
+import threading
+import time
+from typing import Union
+
+from deepgram import DeepgramClient
+from deepgram.core.events import EventType
+from deepgram.speak.v2.types import (
+ SpeakV2Connected,
+ SpeakV2Error,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Speak,
+ SpeakV2SpeechMetadata,
+ SpeakV2SpeechStarted,
+ SpeakV2Warning,
+)
+
+SpeakV2SocketClientResponse = Union[
+ bytes,
+ SpeakV2Connected,
+ SpeakV2SpeechStarted,
+ SpeakV2SpeechMetadata,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Warning,
+ SpeakV2Error,
+]
+
+try:
+ # Using access token instead of API key
+ print("Initializing DeepgramClient for authentication")
+ authClient = DeepgramClient()
+ print("Auth client initialized")
+
+ print("Requesting access token")
+ authResponse = authClient.auth.v1.tokens.grant()
+ print("Access token received successfully")
+ print(f"Token type: {type(authResponse.access_token)}")
+
+ print("Initializing DeepgramClient with access token")
+ client = DeepgramClient(access_token=authResponse.access_token)
+ print("Client initialized with access token")
+
+ model = "flux-alexis-en"
+ encoding = "linear16"
+ sample_rate = "24000"
+ print(f"Establishing connection - Model: {model}, Encoding: {encoding}, Sample Rate: {sample_rate}")
+ with client.speak.v2.connect(model=model, encoding=encoding, sample_rate=sample_rate) as connection:
+ print("Connection context entered")
+
+ def on_message(message: SpeakV2SocketClientResponse) -> None:
+ if isinstance(message, bytes):
+ print("Received audio event")
+ print(f"Event body (audio data length): {len(message)} bytes")
+ else:
+ msg_type = getattr(message, "type", "Unknown")
+ print(f"Received {msg_type} event")
+ print(f"Event body: {message}")
+
+ print("Registering event handlers")
+ connection.on(EventType.OPEN, lambda _: print("Connection opened"))
+ connection.on(EventType.MESSAGE, on_message)
+ connection.on(EventType.CLOSE, lambda _: print("Connection closed"))
+ connection.on(EventType.ERROR, lambda error: print(f"Connection error: {error}"))
+ print("Event handlers registered")
+
+ # EXAMPLE ONLY: Start listening in a background thread for demo purposes
+ # In production, you would typically call connection.start_listening() directly
+ # which blocks until the connection closes, or integrate into your async event loop
+ print("Starting listening thread")
+ threading.Thread(target=connection.start_listening, daemon=True).start()
+
+ # Send text to be converted to speech
+ print("Sending Speak message")
+ connection.send_speak(SpeakV2Speak(text="Hello, world!"))
+
+ # Send control messages
+ print("Sending Flush control message")
+ connection.send_flush()
+ print("Sending Close control message")
+ connection.send_close()
+
+ print("Waiting 3 seconds for events...")
+ time.sleep(3) # EXAMPLE ONLY: Wait briefly to see some events before exiting
+ print("Exiting connection context")
+except Exception as e:
+ print(f"Error occurred: {type(e).__name__}")
+ # Log request headers if available
+ if hasattr(e, "request_headers"):
+ print(f"Request headers: {e.request_headers}")
+ elif hasattr(e, "request") and hasattr(e.request, "headers"):
+ print(f"Request headers: {e.request.headers}")
+ # Log response headers if available
+ if hasattr(e, "headers"):
+ print(f"Response headers: {e.headers}")
+ # Log status code if available
+ if hasattr(e, "status_code"):
+ print(f"Status code: {e.status_code}")
+ # Log body if available
+ if hasattr(e, "body"):
+ print(f"Response body: {e.body}")
+ print(f"Caught: {e}")
+print("Script completed")
diff --git a/tests/manual/speak/v2/connect/with_raw_response.py b/tests/manual/speak/v2/connect/with_raw_response.py
new file mode 100644
index 00000000..310a0f5f
--- /dev/null
+++ b/tests/manual/speak/v2/connect/with_raw_response.py
@@ -0,0 +1,103 @@
+from dotenv import load_dotenv
+
+print("Starting speak v2 connect with raw response example script")
+load_dotenv()
+print("Environment variables loaded")
+
+import threading
+import time
+from typing import Union
+
+from deepgram import DeepgramClient
+from deepgram.core.events import EventType
+from deepgram.speak.v2.types import (
+ SpeakV2Connected,
+ SpeakV2Error,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Speak,
+ SpeakV2SpeechMetadata,
+ SpeakV2SpeechStarted,
+ SpeakV2Warning,
+)
+
+SpeakV2SocketClientResponse = Union[
+ bytes,
+ SpeakV2Connected,
+ SpeakV2SpeechStarted,
+ SpeakV2SpeechMetadata,
+ SpeakV2Flushed,
+ SpeakV2SessionMetadata,
+ SpeakV2Warning,
+ SpeakV2Error,
+]
+
+print("Initializing DeepgramClient")
+client = DeepgramClient()
+print("DeepgramClient initialized")
+
+try:
+ model = "flux-alexis-en"
+ encoding = "linear16"
+ sample_rate = "24000"
+ print(
+ f"Establishing connection with raw response - Model: {model}, Encoding: {encoding}, Sample Rate: {sample_rate}"
+ )
+ with client.speak.v2.with_raw_response.connect(
+ model=model, encoding=encoding, sample_rate=sample_rate
+ ) as connection:
+ print("Connection context entered")
+
+ def on_message(message: SpeakV2SocketClientResponse) -> None:
+ if isinstance(message, bytes):
+ print("Received audio event")
+ print(f"Event body (audio data length): {len(message)} bytes")
+ else:
+ msg_type = getattr(message, "type", "Unknown")
+ print(f"Received {msg_type} event")
+ print(f"Event body: {message}")
+
+ print("Registering event handlers")
+ connection.on(EventType.OPEN, lambda _: print("Connection opened"))
+ connection.on(EventType.MESSAGE, on_message)
+ connection.on(EventType.CLOSE, lambda _: print("Connection closed"))
+ connection.on(EventType.ERROR, lambda error: print(f"Connection error: {error}"))
+ print("Event handlers registered")
+
+ # EXAMPLE ONLY: Start listening in a background thread for demo purposes
+ # In production, you would typically call connection.start_listening() directly
+ # which blocks until the connection closes, or integrate into your async event loop
+ print("Starting listening thread")
+ threading.Thread(target=connection.start_listening, daemon=True).start()
+
+ # Send text to be converted to speech
+ print("Sending Speak message")
+ connection.send_speak(SpeakV2Speak(text="Hello, world!"))
+
+ # Send control messages
+ print("Sending Flush control message")
+ connection.send_flush()
+ print("Sending Close control message")
+ connection.send_close()
+
+ print("Waiting 3 seconds for events...")
+ time.sleep(3) # EXAMPLE ONLY: Wait briefly to see some events before exiting
+ print("Exiting connection context")
+except Exception as e:
+ print(f"Error occurred: {type(e).__name__}")
+ # Log request headers if available
+ if hasattr(e, "request_headers"):
+ print(f"Request headers: {e.request_headers}")
+ elif hasattr(e, "request") and hasattr(e.request, "headers"):
+ print(f"Request headers: {e.request.headers}")
+ # Log response headers if available
+ if hasattr(e, "headers"):
+ print(f"Response headers: {e.headers}")
+ # Log status code if available
+ if hasattr(e, "status_code"):
+ print(f"Status code: {e.status_code}")
+ # Log body if available
+ if hasattr(e, "body"):
+ print(f"Response body: {e.body}")
+ print(f"Caught: {e}")
+print("Script completed")