From c5082a7edf5abaee0b105cc924bfe2c514c53af3 Mon Sep 17 00:00:00 2001 From: David vonThenen <12752197+dvonthenen@users.noreply.github.com> Date: Mon, 14 Oct 2024 12:11:46 -0700 Subject: [PATCH] Agent API Early Access --- deepgram/__init__.py | 52 +- deepgram/audio/microphone/microphone.py | 140 ++-- deepgram/audio/speaker/speaker.py | 115 +-- deepgram/client.py | 66 +- deepgram/clients/__init__.py | 56 +- deepgram/clients/agent/__init__.py | 51 ++ deepgram/clients/agent/client.py | 92 +++ deepgram/clients/agent/enums.py | 35 + deepgram/clients/agent/v1/__init__.py | 55 ++ .../clients/agent/v1/websocket/__init__.py | 46 ++ .../agent/v1/websocket/async_client.py | 670 ++++++++++++++++++ deepgram/clients/agent/v1/websocket/client.py | 654 +++++++++++++++++ .../clients/agent/v1/websocket/options.py | 319 +++++++++ .../clients/agent/v1/websocket/response.py | 96 +++ deepgram/clients/agent_router.py | 130 ++++ .../clients/common/v1/abstract_async_rest.py | 7 + .../common/v1/abstract_async_websocket.py | 12 +- .../clients/common/v1/abstract_sync_rest.py | 7 + .../common/v1/abstract_sync_websocket.py | 12 +- deepgram/clients/listen/v1/helpers.py | 53 -- deepgram/clients/listen_router.py | 3 +- deepgram/clients/read_router.py | 7 +- .../speak/v1/websocket/async_client.py | 18 +- deepgram/clients/speak/v1/websocket/client.py | 18 +- .../clients/speak/v1/websocket/helpers.py | 53 -- deepgram/clients/speak_router.py | 2 +- examples/agent/async_simple/main.py | 152 ++++ examples/agent/simple/main.py | 127 ++++ .../speech-to-text/websocket/replay/main.py | 100 +++ .../replay/microsoft_headquarters.wav | Bin 0 -> 224434 bytes .../{async_complete => async_simple}/main.py | 0 .../{complete => output_to_wav}/main.py | 72 +- .../text-to-speech/websocket/simple/main.py | 72 +- 33 files changed, 2981 insertions(+), 311 deletions(-) create mode 100644 deepgram/clients/agent/__init__.py create mode 100644 deepgram/clients/agent/client.py create mode 100644 deepgram/clients/agent/enums.py create mode 100644 deepgram/clients/agent/v1/__init__.py create mode 100644 deepgram/clients/agent/v1/websocket/__init__.py create mode 100644 deepgram/clients/agent/v1/websocket/async_client.py create mode 100644 deepgram/clients/agent/v1/websocket/client.py create mode 100644 deepgram/clients/agent/v1/websocket/options.py create mode 100644 deepgram/clients/agent/v1/websocket/response.py create mode 100644 deepgram/clients/agent_router.py delete mode 100644 deepgram/clients/listen/v1/helpers.py delete mode 100644 deepgram/clients/speak/v1/websocket/helpers.py create mode 100644 examples/agent/async_simple/main.py create mode 100644 examples/agent/simple/main.py create mode 100644 examples/speech-to-text/websocket/replay/main.py create mode 100644 examples/speech-to-text/websocket/replay/microsoft_headquarters.wav rename examples/text-to-speech/websocket/{async_complete => async_simple}/main.py (100%) rename examples/text-to-speech/websocket/{complete => output_to_wav}/main.py (53%) diff --git a/deepgram/__init__.py b/deepgram/__init__.py index 1d7568fe..d810f449 100644 --- a/deepgram/__init__.py +++ b/deepgram/__init__.py @@ -34,7 +34,7 @@ from .errors import DeepgramApiKeyError # listen/read client -from .client import Listen, Read +from .client import ListenRouter, ReadRouter, SpeakRouter, AgentRouter # common from .client import ( @@ -302,6 +302,56 @@ AsyncSelfHostedClient, ) + +# agent +from .client import AgentWebSocketEvents + +# websocket +from .client import ( + AgentWebSocketClient, + AsyncAgentWebSocketClient, +) + +from .client import ( + #### common websocket response + # OpenResponse, + # CloseResponse, + # ErrorResponse, + # UnhandledResponse, + #### unique + WelcomeResponse, + SettingsAppliedResponse, + ConversationTextResponse, + UserStartedSpeakingResponse, + AgentThinkingResponse, + FunctionCallingResponse, + AgentStartedSpeakingResponse, + AgentAudioDoneResponse, +) + +from .client import ( + # top level + SettingsConfigurationOptions, + UpdateInstructionsOptions, + UpdateSpeakOptions, + InjectAgentMessageOptions, + # sub level + Listen, + Speak, + Header, + Item, + Properties, + Parameters, + Function, + Provider, + Think, + Agent, + Input, + Output, + Audio, + Context, +) + # utilities # pylint: disable=wrong-import-position from .audio import Microphone, DeepgramMicrophoneError diff --git a/deepgram/audio/microphone/microphone.py b/deepgram/audio/microphone/microphone.py index ae452984..693c97a9 100644 --- a/deepgram/audio/microphone/microphone.py +++ b/deepgram/audio/microphone/microphone.py @@ -9,6 +9,7 @@ import logging from ...utils import verboselogs + from .constants import LOGGING, CHANNELS, RATE, CHUNK if TYPE_CHECKING: @@ -22,8 +23,8 @@ class Microphone: # pylint: disable=too-many-instance-attributes _logger: verboselogs.VerboseLogger - _audio: "pyaudio.PyAudio" - _stream: "pyaudio.Stream" + _audio: Optional["pyaudio.PyAudio"] = None + _stream: Optional["pyaudio.Stream"] = None _chunk: int _rate: int @@ -145,59 +146,31 @@ def start(self) -> bool: self._asyncio_thread = None self._push_callback = self._push_callback_org - self._stream = self._audio.open( - format=self._format, - channels=self._channels, - rate=self._rate, - input=True, - frames_per_buffer=self._chunk, - input_device_index=self._input_device_index, - stream_callback=self._callback, - ) + if self._audio is not None: + self._stream = self._audio.open( + format=self._format, + channels=self._channels, + rate=self._rate, + input=True, + output=False, + frames_per_buffer=self._chunk, + input_device_index=self._input_device_index, + stream_callback=self._callback, + ) + + if self._stream is None: + self._logger.error("start failed. No stream created.") + self._logger.debug("Microphone.start LEAVE") + return False self._exit.clear() - self._stream.start_stream() + if self._stream is not None: + self._stream.start_stream() self._logger.notice("start succeeded") self._logger.debug("Microphone.start LEAVE") return True - def _callback( - self, input_data, frame_count, time_info, status_flags - ): # pylint: disable=unused-argument - """ - The callback used to process data in callback mode. - """ - # dynamic import of pyaudio as not to force the requirements on the SDK (and users) - import pyaudio # pylint: disable=import-outside-toplevel - - self._logger.debug("Microphone._callback ENTER") - - if self._exit.is_set(): - self._logger.info("exit is Set") - self._logger.notice("_callback stopping...") - self._logger.debug("Microphone._callback LEAVE") - return None, pyaudio.paAbort - - if input_data is None: - self._logger.warning("input_data is None") - self._logger.debug("Microphone._callback LEAVE") - return None, pyaudio.paContinue - - try: - if self._is_muted: - size = len(input_data) - input_data = b"\x00" * size - - self._push_callback(input_data) - except Exception as e: - self._logger.error("Error while sending: %s", str(e)) - self._logger.debug("Microphone._callback LEAVE") - raise - - self._logger.debug("Microphone._callback LEAVE") - return input_data, pyaudio.paContinue - def mute(self) -> bool: """ mute - mutes the microphone stream @@ -205,17 +178,17 @@ def mute(self) -> bool: Returns: bool: True if the stream was muted, False otherwise """ - self._logger.debug("Microphone.mute ENTER") + self._logger.verbose("Microphone.mute ENTER") if self._stream is None: self._logger.error("mute failed. Library not initialized.") - self._logger.debug("Microphone.mute LEAVE") + self._logger.verbose("Microphone.mute LEAVE") return False self._is_muted = True self._logger.notice("mute succeeded") - self._logger.debug("Microphone.mute LEAVE") + self._logger.verbose("Microphone.mute LEAVE") return True def unmute(self) -> bool: @@ -225,19 +198,42 @@ def unmute(self) -> bool: Returns: bool: True if the stream was unmuted, False otherwise """ - self._logger.debug("Microphone.unmute ENTER") + self._logger.verbose("Microphone.unmute ENTER") if self._stream is None: self._logger.error("unmute failed. Library not initialized.") - self._logger.debug("Microphone.unmute LEAVE") + self._logger.verbose("Microphone.unmute LEAVE") return False self._is_muted = False self._logger.notice("unmute succeeded") - self._logger.debug("Microphone.unmute LEAVE") + self._logger.verbose("Microphone.unmute LEAVE") return True + def is_muted(self) -> bool: + """ + is_muted - returns the state of the stream + + Args: + None + + Returns: + True if the stream is muted, False otherwise + """ + self._logger.spam("Microphone.is_muted ENTER") + + if self._stream is None: + self._logger.spam("is_muted: stream is None") + self._logger.spam("Microphone.is_muted LEAVE") + return False + + val = self._is_muted + + self._logger.spam("is_muted: %s", val) + self._logger.spam("Microphone.is_muted LEAVE") + return val + def finish(self) -> bool: """ finish - stops the microphone stream @@ -255,7 +251,6 @@ def finish(self) -> bool: self._logger.notice("stopping stream...") self._stream.stop_stream() self._stream.close() - self._stream = None # type: ignore self._logger.notice("stream stopped") # clean up the thread @@ -265,13 +260,44 @@ def finish(self) -> bool: self._asyncio_thread is not None ): - self._logger.notice("stopping asyncio loop...") + self._logger.notice("stopping _asyncio_loop...") self._asyncio_loop.call_soon_threadsafe(self._asyncio_loop.stop) self._asyncio_thread.join() - self._asyncio_thread = None self._logger.notice("_asyncio_thread joined") + self._stream = None + self._asyncio_thread = None + self._logger.notice("finish succeeded") self._logger.debug("Microphone.finish LEAVE") return True + + def _callback( + self, input_data, frame_count, time_info, status_flags + ): # pylint: disable=unused-argument + """ + The callback used to process data in callback mode. + """ + # dynamic import of pyaudio as not to force the requirements on the SDK (and users) + import pyaudio # pylint: disable=import-outside-toplevel + + if self._exit.is_set(): + self._logger.notice("_callback exit is Set. stopping...") + return None, pyaudio.paAbort + + if input_data is None: + self._logger.warning("input_data is None") + return None, pyaudio.paContinue + + try: + if self._is_muted: + size = len(input_data) + input_data = b"\x00" * size + + self._push_callback(input_data) + except Exception as e: + self._logger.error("Error while sending: %s", str(e)) + raise + + return input_data, pyaudio.paContinue diff --git a/deepgram/audio/speaker/speaker.py b/deepgram/audio/speaker/speaker.py index faee51c4..e3464675 100644 --- a/deepgram/audio/speaker/speaker.py +++ b/deepgram/audio/speaker/speaker.py @@ -15,6 +15,8 @@ from ...utils import verboselogs from .constants import LOGGING, CHANNELS, RATE, CHUNK, TIMEOUT, PLAYBACK_DELTA +from ..microphone import Microphone + if TYPE_CHECKING: import pyaudio @@ -28,7 +30,7 @@ class Speaker: # pylint: disable=too-many-instance-attributes _logger: verboselogs.VerboseLogger - _audio: "pyaudio.PyAudio" + _audio: Optional["pyaudio.PyAudio"] = None _stream: Optional["pyaudio.Stream"] = None _chunk: int @@ -48,7 +50,6 @@ class Speaker: # pylint: disable=too-many-instance-attributes # _asyncio_loop: asyncio.AbstractEventLoop # _asyncio_thread: threading.Thread _receiver_thread: Optional[threading.Thread] = None - _loop: Optional[asyncio.AbstractEventLoop] = None _push_callback_org: Optional[Callable] = None @@ -56,6 +57,8 @@ class Speaker: # pylint: disable=too-many-instance-attributes _pull_callback_org: Optional[Callable] = None _pull_callback: Optional[Callable] = None + _microphone: Optional[Microphone] = None + def __init__( self, pull_callback: Optional[Callable] = None, @@ -66,6 +69,7 @@ def __init__( channels: int = CHANNELS, last_play_delta_in_ms: int = PLAYBACK_DELTA, output_device_index: Optional[int] = None, + microphone: Optional[Microphone] = None, ): # pylint: disable=too-many-positional-arguments # dynamic import of pyaudio as not to force the requirements on the SDK (and users) import pyaudio # pylint: disable=import-outside-toplevel @@ -80,6 +84,8 @@ def __init__( self._last_datagram = datetime.now() self._lock_wait = threading.Lock() + self._microphone = microphone + self._audio = pyaudio.PyAudio() self._chunk = chunk self._rate = rate @@ -117,10 +123,6 @@ def set_pull_callback(self, pull_callback: Callable) -> None: """ self._pull_callback_org = pull_callback - # def _start_asyncio_loop(self) -> None: - # self._asyncio_loop = asyncio.new_event_loop() - # self._asyncio_loop.run_forever() - def start(self, active_loop: Optional[asyncio.AbstractEventLoop] = None) -> bool: """ starts - starts the Speaker stream @@ -147,45 +149,25 @@ def start(self, active_loop: Optional[asyncio.AbstractEventLoop] = None) -> bool self._exit.clear() self._queue = queue.Queue() - self._stream = self._audio.open( - format=self._format, - channels=self._channels, - rate=self._rate, - input=False, - output=True, - frames_per_buffer=self._chunk, - output_device_index=self._output_device_index, - ) + if self._audio is not None: + self._stream = self._audio.open( + format=self._format, + channels=self._channels, + rate=self._rate, + input=False, + output=True, + frames_per_buffer=self._chunk, + output_device_index=self._output_device_index, + ) + + if self._stream is None: + self._logger.error("start failed. No stream created.") + self._logger.debug("Speaker.start LEAVE") + return False self._push_callback = self._push_callback_org self._pull_callback = self._pull_callback_org - # if inspect.iscoroutinefunction( - # self._push_callback_org - # ) or inspect.iscoroutinefunction(self._pull_callback_org): - # self._logger.verbose("Starting asyncio loop...") - # self._asyncio_thread = threading.Thread(target=self._start_asyncio_loop) - # self._asyncio_thread.start() - - # # determine if the push_callback is a coroutine - # if inspect.iscoroutinefunction(self._push_callback_org): - # self._logger.verbose("async/await push callback") - # self._push_callback = lambda data: asyncio.run_coroutine_threadsafe( - # self._push_callback_org(data), self._asyncio_loop - # ).result() - # else: - # self._logger.verbose("threaded push callback") - # self._push_callback = self._push_callback_org - - # if inspect.iscoroutinefunction(self._pull_callback_org): - # self._logger.verbose("async/await pull callback") - # self._pull_callback = lambda: asyncio.run_coroutine_threadsafe( - # self._pull_callback_org(), self._asyncio_loop - # ).result() - # else: - # self._logger.verbose("threaded pull callback") - # self._pull_callback = self._pull_callback_org - # start the play thread self._thread = threading.Thread( target=self._play, args=(self._queue, self._stream, self._exit), daemon=True @@ -193,7 +175,8 @@ def start(self, active_loop: Optional[asyncio.AbstractEventLoop] = None) -> bool self._thread.start() # Start the stream - self._stream.start_stream() + if self._stream is not None: + self._stream.start_stream() # Start the receiver thread within the start function self._logger.verbose("Starting receiver thread...") @@ -205,6 +188,20 @@ def start(self, active_loop: Optional[asyncio.AbstractEventLoop] = None) -> bool return True + def wait_for_complete_with_mute(self, mic: Microphone): + """ + This method will mute/unmute a Microphone and block until the speak is done playing sound. + """ + self._logger.debug("Speaker.wait_for_complete ENTER") + + if self._microphone is not None: + mic.mute() + self.wait_for_complete() + if self._microphone is not None: + mic.unmute() + + self._logger.debug("Speaker.wait_for_complete LEAVE") + def wait_for_complete(self): """ This method will block until the speak is done playing sound. @@ -328,29 +325,24 @@ def finish(self) -> bool: self._logger.notice("stopping stream...") self._stream.stop_stream() self._stream.close() - self._stream = None self._logger.notice("stream stopped") if self._thread is not None: - self._logger.notice("joining thread...") + self._logger.notice("joining _thread...") self._thread.join() - self._thread = None self._logger.notice("thread stopped") - # if self._asyncio_thread is not None: - # self._logger.notice("stopping asyncio loop...") - # self._asyncio_loop.call_soon_threadsafe(self._asyncio_loop.stop) - # self._asyncio_thread.join() - # self._asyncio_thread = None - # self._logger.notice("_asyncio_thread joined") - if self._receiver_thread is not None: - self._logger.notice("stopping asyncio loop...") + self._logger.notice("stopping _receiver_thread...") self._receiver_thread.join() - self._receiver_thread = None self._logger.notice("_receiver_thread joined") - self._queue = None # type: ignore + with self._queue.mutex: + self._queue.queue.clear() + + self._stream = None + self._thread = None + self._receiver_thread = None self._logger.notice("finish succeeded") self._logger.debug("Speaker.finish LEAVE") @@ -363,9 +355,22 @@ def _play(self, audio_out, stream, stop): """ while not stop.is_set(): try: + if self._microphone is not None and self._microphone.is_muted(): + with self._lock_wait: + delta = datetime.now() - self._last_datagram + diff_in_ms = delta.total_seconds() * 1000 + if diff_in_ms > float(self._last_play_delta_in_ms): + self._logger.debug( + "LastPlay delta is greater than threshold. Unmute!" + ) + self._microphone.unmute() + data = audio_out.get(True, TIMEOUT) with self._lock_wait: self._last_datagram = datetime.now() + if self._microphone is not None and not self._microphone.is_muted(): + self._logger.debug("New speaker sound detected. Mute!") + self._microphone.mute() stream.write(data) except queue.Empty: pass diff --git a/deepgram/client.py b/deepgram/client.py index 08ef1717..d7dccbd2 100644 --- a/deepgram/client.py +++ b/deepgram/client.py @@ -55,7 +55,7 @@ ) # listen client -from .clients import Listen, Read, Speak +from .clients import ListenRouter, ReadRouter, SpeakRouter, AgentRouter # speech-to-text from .clients import LiveClient, AsyncLiveClient # backward compat @@ -308,6 +308,57 @@ AsyncSelfHostedClient, ) + +# agent +from .clients import AgentWebSocketEvents + +# websocket +from .clients import ( + AgentWebSocketClient, + AsyncAgentWebSocketClient, +) + +from .clients import ( + #### common websocket response + # OpenResponse, + # CloseResponse, + # ErrorResponse, + # UnhandledResponse, + #### unique + WelcomeResponse, + SettingsAppliedResponse, + ConversationTextResponse, + UserStartedSpeakingResponse, + AgentThinkingResponse, + FunctionCallingResponse, + AgentStartedSpeakingResponse, + AgentAudioDoneResponse, +) + +from .clients import ( + # top level + SettingsConfigurationOptions, + UpdateInstructionsOptions, + UpdateSpeakOptions, + InjectAgentMessageOptions, + # sub level + Listen, + Speak, + Header, + Item, + Properties, + Parameters, + Function, + Provider, + Think, + Agent, + Input, + Output, + Audio, + Context, +) + + # client errors and options from .options import DeepgramClientOptions, ClientOptionsFromEnv from .errors import DeepgramApiKeyError @@ -397,21 +448,21 @@ def listen(self): """ Returns a Listen dot-notation router for interacting with Deepgram's transcription services. """ - return Listen(self._config) + return ListenRouter(self._config) @property def read(self): """ Returns a Read dot-notation router for interacting with Deepgram's read services. """ - return Read(self._config) + return ReadRouter(self._config) @property def speak(self): """ Returns a Speak dot-notation router for interacting with Deepgram's speak services. """ - return Speak(self._config) + return SpeakRouter(self._config) @property @deprecation.deprecated( @@ -480,6 +531,13 @@ def asyncselfhosted(self): """ return self.Version(self._config, "asyncselfhosted") + @property + def agent(self): + """ + Returns a Agent dot-notation router for interacting with Deepgram's speak services. + """ + return AgentRouter(self._config) + # INTERNAL CLASSES class Version: """ diff --git a/deepgram/clients/__init__.py b/deepgram/clients/__init__.py index 01ec90ee..7d037549 100644 --- a/deepgram/clients/__init__.py +++ b/deepgram/clients/__init__.py @@ -48,9 +48,10 @@ ) from .errors import DeepgramModuleError -from .listen_router import Listen -from .read_router import Read -from .speak_router import Speak +from .listen_router import ListenRouter +from .read_router import ReadRouter +from .speak_router import SpeakRouter +from .agent_router import AgentRouter # listen from .listen import LiveTranscriptionEvents @@ -318,3 +319,52 @@ SelfHostedClient, AsyncSelfHostedClient, ) + +# agent +from .agent import AgentWebSocketEvents + +# websocket +from .agent import ( + AgentWebSocketClient, + AsyncAgentWebSocketClient, +) + +from .agent import ( + #### common websocket response + # OpenResponse, + # CloseResponse, + # ErrorResponse, + # UnhandledResponse, + #### unique + WelcomeResponse, + SettingsAppliedResponse, + ConversationTextResponse, + UserStartedSpeakingResponse, + AgentThinkingResponse, + FunctionCallingResponse, + AgentStartedSpeakingResponse, + AgentAudioDoneResponse, +) + +from .agent import ( + # top level + SettingsConfigurationOptions, + UpdateInstructionsOptions, + UpdateSpeakOptions, + InjectAgentMessageOptions, + # sub level + Listen, + Speak, + Header, + Item, + Properties, + Parameters, + Function, + Provider, + Think, + Agent, + Input, + Output, + Audio, + Context, +) diff --git a/deepgram/clients/agent/__init__.py b/deepgram/clients/agent/__init__.py new file mode 100644 index 00000000..7997477a --- /dev/null +++ b/deepgram/clients/agent/__init__.py @@ -0,0 +1,51 @@ +# Copyright 2023-2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +from .enums import AgentWebSocketEvents + +# websocket +from .client import ( + AgentWebSocketClient, + AsyncAgentWebSocketClient, +) + +from .client import ( + #### common websocket response + OpenResponse, + CloseResponse, + ErrorResponse, + UnhandledResponse, + #### unique + WelcomeResponse, + SettingsAppliedResponse, + ConversationTextResponse, + UserStartedSpeakingResponse, + AgentThinkingResponse, + FunctionCallingResponse, + AgentStartedSpeakingResponse, + AgentAudioDoneResponse, +) + +from .client import ( + # top level + SettingsConfigurationOptions, + UpdateInstructionsOptions, + UpdateSpeakOptions, + InjectAgentMessageOptions, + # sub level + Listen, + Speak, + Header, + Item, + Properties, + Parameters, + Function, + Provider, + Think, + Agent, + Input, + Output, + Audio, + Context, +) diff --git a/deepgram/clients/agent/client.py b/deepgram/clients/agent/client.py new file mode 100644 index 00000000..239129da --- /dev/null +++ b/deepgram/clients/agent/client.py @@ -0,0 +1,92 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +# websocket +from .v1 import ( + AgentWebSocketClient as LatestAgentWebSocketClient, + AsyncAgentWebSocketClient as LatestAsyncAgentWebSocketClient, +) + +from .v1 import ( + #### common websocket response + BaseResponse as LatestBaseResponse, + OpenResponse as LatestOpenResponse, + CloseResponse as LatestCloseResponse, + ErrorResponse as LatestErrorResponse, + UnhandledResponse as LatestUnhandledResponse, + #### unique + WelcomeResponse as LatestWelcomeResponse, + SettingsAppliedResponse as LatestSettingsAppliedResponse, + ConversationTextResponse as LatestConversationTextResponse, + UserStartedSpeakingResponse as LatestUserStartedSpeakingResponse, + AgentThinkingResponse as LatestAgentThinkingResponse, + FunctionCallingResponse as LatestFunctionCallingResponse, + AgentStartedSpeakingResponse as LatestAgentStartedSpeakingResponse, + AgentAudioDoneResponse as LatestAgentAudioDoneResponse, +) + +from .v1 import ( + # top level + SettingsConfigurationOptions as LatestSettingsConfigurationOptions, + UpdateInstructionsOptions as LatestUpdateInstructionsOptions, + UpdateSpeakOptions as LatestUpdateSpeakOptions, + InjectAgentMessageOptions as LatestInjectAgentMessageOptions, + # sub level + Listen as LatestListen, + Speak as LatestSpeak, + Header as LatestHeader, + Item as LatestItem, + Properties as LatestProperties, + Parameters as LatestParameters, + Function as LatestFunction, + Provider as LatestProvider, + Think as LatestThink, + Agent as LatestAgent, + Input as LatestInput, + Output as LatestOutput, + Audio as LatestAudio, + Context as LatestContext, +) + + +# The vX/client.py points to the current supported version in the SDK. +# Older versions are supported in the SDK for backwards compatibility. + +AgentWebSocketClient = LatestAgentWebSocketClient +AsyncAgentWebSocketClient = LatestAsyncAgentWebSocketClient + +OpenResponse = LatestOpenResponse +CloseResponse = LatestCloseResponse +ErrorResponse = LatestErrorResponse +UnhandledResponse = LatestUnhandledResponse + +WelcomeResponse = LatestWelcomeResponse +SettingsAppliedResponse = LatestSettingsAppliedResponse +ConversationTextResponse = LatestConversationTextResponse +UserStartedSpeakingResponse = LatestUserStartedSpeakingResponse +AgentThinkingResponse = LatestAgentThinkingResponse +FunctionCallingResponse = LatestFunctionCallingResponse +AgentStartedSpeakingResponse = LatestAgentStartedSpeakingResponse +AgentAudioDoneResponse = LatestAgentAudioDoneResponse + + +SettingsConfigurationOptions = LatestSettingsConfigurationOptions +UpdateInstructionsOptions = LatestUpdateInstructionsOptions +UpdateSpeakOptions = LatestUpdateSpeakOptions +InjectAgentMessageOptions = LatestInjectAgentMessageOptions + +Listen = LatestListen +Speak = LatestSpeak +Header = LatestHeader +Item = LatestItem +Properties = LatestProperties +Parameters = LatestParameters +Function = LatestFunction +Provider = LatestProvider +Think = LatestThink +Agent = LatestAgent +Input = LatestInput +Output = LatestOutput +Audio = LatestAudio +Context = LatestContext diff --git a/deepgram/clients/agent/enums.py b/deepgram/clients/agent/enums.py new file mode 100644 index 00000000..4708f3af --- /dev/null +++ b/deepgram/clients/agent/enums.py @@ -0,0 +1,35 @@ +# Copyright 2023-2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +from aenum import StrEnum + +# Constants mapping to events from the Deepgram API + + +class AgentWebSocketEvents(StrEnum): + """ + Enumerates the possible Agent API events that can be received from the Deepgram API + """ + + # server + Open: str = "Open" + Close: str = "Close" + AudioData: str = "AudioData" + Welcome: str = "Welcome" + SettingsApplied: str = "SettingsApplied" + ConversationText: str = "ConversationText" + UserStartedSpeaking: str = "UserStartedSpeaking" + AgentThinking: str = "AgentThinking" + FunctionCalling: str = "FunctionCalling" + AgentStartedSpeaking: str = "AgentStartedSpeaking" + AgentAudioDone: str = "AgentAudioDone" + Error: str = "Error" + Unhandled: str = "Unhandled" + + # client + SettingsConfiguration: str = "SettingsConfiguration" + UpdateInstructions: str = "UpdateInstructions" + UpdateSpeak: str = "UpdateSpeak" + InjectAgentMessage: str = "InjectAgentMessage" + KeepAlive: str = "KeepAlive" diff --git a/deepgram/clients/agent/v1/__init__.py b/deepgram/clients/agent/v1/__init__.py new file mode 100644 index 00000000..2c2768a1 --- /dev/null +++ b/deepgram/clients/agent/v1/__init__.py @@ -0,0 +1,55 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +# common websocket +from ...common import ( + OpenResponse, + CloseResponse, + UnhandledResponse, + ErrorResponse, +) + +# websocket +from .websocket import AgentWebSocketClient, AsyncAgentWebSocketClient + +from .websocket import ( + #### common websocket response + BaseResponse, + OpenResponse, + CloseResponse, + ErrorResponse, + UnhandledResponse, + #### unique + WelcomeResponse, + SettingsAppliedResponse, + ConversationTextResponse, + UserStartedSpeakingResponse, + AgentThinkingResponse, + FunctionCallingResponse, + AgentStartedSpeakingResponse, + AgentAudioDoneResponse, +) + +from .websocket import ( + # top level + SettingsConfigurationOptions, + UpdateInstructionsOptions, + UpdateSpeakOptions, + InjectAgentMessageOptions, + # sub level + Listen, + Speak, + Header, + Item, + Properties, + Parameters, + Function, + Provider, + Think, + Agent, + Input, + Output, + Audio, + Context, +) diff --git a/deepgram/clients/agent/v1/websocket/__init__.py b/deepgram/clients/agent/v1/websocket/__init__.py new file mode 100644 index 00000000..25c72ef4 --- /dev/null +++ b/deepgram/clients/agent/v1/websocket/__init__.py @@ -0,0 +1,46 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +from .client import AgentWebSocketClient +from .async_client import AsyncAgentWebSocketClient + +from .response import ( + #### common websocket response + BaseResponse, + OpenResponse, + CloseResponse, + ErrorResponse, + UnhandledResponse, + #### unique + WelcomeResponse, + SettingsAppliedResponse, + ConversationTextResponse, + UserStartedSpeakingResponse, + AgentThinkingResponse, + FunctionCallingResponse, + AgentStartedSpeakingResponse, + AgentAudioDoneResponse, +) +from .options import ( + # top level + SettingsConfigurationOptions, + UpdateInstructionsOptions, + UpdateSpeakOptions, + InjectAgentMessageOptions, + # sub level + Listen, + Speak, + Header, + Item, + Properties, + Parameters, + Function, + Provider, + Think, + Agent, + Input, + Output, + Audio, + Context, +) diff --git a/deepgram/clients/agent/v1/websocket/async_client.py b/deepgram/clients/agent/v1/websocket/async_client.py new file mode 100644 index 00000000..46dabca2 --- /dev/null +++ b/deepgram/clients/agent/v1/websocket/async_client.py @@ -0,0 +1,670 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +import asyncio +import json +import logging +from typing import Dict, Union, Optional, cast, Any, Callable +import threading + +from .....utils import verboselogs +from .....options import DeepgramClientOptions +from ...enums import AgentWebSocketEvents +from ....common import AbstractAsyncWebSocketClient +from ....common import DeepgramError + +from .response import ( + OpenResponse, + WelcomeResponse, + SettingsAppliedResponse, + ConversationTextResponse, + UserStartedSpeakingResponse, + AgentThinkingResponse, + FunctionCallingResponse, + AgentStartedSpeakingResponse, + AgentAudioDoneResponse, + CloseResponse, + ErrorResponse, + UnhandledResponse, +) +from .options import ( + SettingsConfigurationOptions, + UpdateInstructionsOptions, + UpdateSpeakOptions, + InjectAgentMessageOptions, +) + +from .....audio.speaker import ( + Speaker, + RATE as SPEAKER_RATE, + CHANNELS as SPEAKER_CHANNELS, + PLAYBACK_DELTA as SPEAKER_PLAYBACK_DELTA, +) +from .....audio.microphone import ( + Microphone, + RATE as MICROPHONE_RATE, + CHANNELS as MICROPHONE_CHANNELS, +) + +ONE_SECOND = 1 +HALF_SECOND = 0.5 +DEEPGRAM_INTERVAL = 5 + + +class AsyncAgentWebSocketClient( + AbstractAsyncWebSocketClient +): # pylint: disable=too-many-instance-attributes + """ + Client for interacting with Deepgram's live transcription services over WebSockets. + + This class provides methods to establish a WebSocket connection for live transcription and handle real-time transcription events. + + Args: + config (DeepgramClientOptions): all the options for the client. + """ + + _logger: verboselogs.VerboseLogger + _config: DeepgramClientOptions + _endpoint: str + + _event_handlers: Dict[AgentWebSocketEvents, list] + + _keep_alive_thread: Union[asyncio.Task, None] + + _kwargs: Optional[Dict] = None + _addons: Optional[Dict] = None + # note the distinction here. We can't use _config because it's already used in the parent + _settings: Optional[SettingsConfigurationOptions] = None + _headers: Optional[Dict] = None + + _speaker_created: bool = False + _speaker: Optional[Speaker] = None + _microphone_created: bool = False + _microphone: Optional[Microphone] = None + + def __init__(self, config: DeepgramClientOptions): + if config is None: + raise DeepgramError("Config is required") + + self._logger = verboselogs.VerboseLogger(__name__) + self._logger.addHandler(logging.StreamHandler()) + self._logger.setLevel(config.verbose) + + self._config = config + + # needs to be "wss://agent.deepgram.com/agent" + self._endpoint = "agent" + + # override the endpoint since it needs to be "wss://agent.deepgram.com/agent" + self._config.url = "agent.deepgram.com" + self._keep_alive_thread = None + + # init handlers + self._event_handlers = { + event: [] for event in AgentWebSocketEvents.__members__.values() + } + + if self._config.options.get("microphone_record") == "true": + self._logger.info("microphone_record is enabled") + rate = self._config.options.get("microphone_record_rate", MICROPHONE_RATE) + channels = self._config.options.get( + "microphone_record_channels", MICROPHONE_CHANNELS + ) + device_index = self._config.options.get("microphone_record_device_index") + + self._logger.debug("rate: %s", rate) + self._logger.debug("channels: %s", channels) + if device_index is not None: + self._logger.debug("device_index: %s", device_index) + + self._microphone_created = True + + if device_index is not None: + self._microphone = Microphone( + rate=rate, + channels=channels, + verbose=self._config.verbose, + input_device_index=device_index, + ) + else: + self._microphone = Microphone( + rate=rate, + channels=channels, + verbose=self._config.verbose, + ) + + if self._config.options.get("speaker_playback") == "true": + self._logger.info("speaker_playback is enabled") + rate = self._config.options.get("speaker_playback_rate", SPEAKER_RATE) + channels = self._config.options.get( + "speaker_playback_channels", SPEAKER_CHANNELS + ) + playback_delta_in_ms = self._config.options.get( + "speaker_playback_delta_in_ms", SPEAKER_PLAYBACK_DELTA + ) + device_index = self._config.options.get("speaker_playback_device_index") + + self._logger.debug("rate: %s", rate) + self._logger.debug("channels: %s", channels) + + self._speaker_created = True + + if device_index is not None: + self._logger.debug("device_index: %s", device_index) + + self._speaker = Speaker( + rate=rate, + channels=channels, + last_play_delta_in_ms=playback_delta_in_ms, + verbose=self._config.verbose, + output_device_index=device_index, + microphone=self._microphone, + ) + else: + self._speaker = Speaker( + rate=rate, + channels=channels, + last_play_delta_in_ms=playback_delta_in_ms, + verbose=self._config.verbose, + microphone=self._microphone, + ) + # call the parent constructor + super().__init__(self._config, self._endpoint) + + # pylint: disable=too-many-branches,too-many-statements + async def start( + self, + options: Optional[SettingsConfigurationOptions] = None, + addons: Optional[Dict] = None, + headers: Optional[Dict] = None, + members: Optional[Dict] = None, + **kwargs, + ) -> bool: + """ + Starts the WebSocket connection for agent API. + """ + self._logger.debug("AsyncAgentWebSocketClient.start ENTER") + self._logger.info("settings: %s", options) + self._logger.info("addons: %s", addons) + self._logger.info("headers: %s", headers) + self._logger.info("members: %s", members) + self._logger.info("kwargs: %s", kwargs) + + if isinstance(options, SettingsConfigurationOptions) and not options.check(): + self._logger.error("settings.check failed") + self._logger.debug("AsyncAgentWebSocketClient.start LEAVE") + raise DeepgramError("Fatal agent settings error") + + self._addons = addons + self._headers = headers + + # add "members" as members of the class + if members is not None: + self.__dict__.update(members) + + # set kwargs as members of the class + if kwargs is not None: + self._kwargs = kwargs + else: + self._kwargs = {} + + if isinstance(options, SettingsConfigurationOptions): + self._logger.info("options is class") + self._settings = options + elif isinstance(options, dict): + self._logger.info("options is dict") + self._settings = SettingsConfigurationOptions.from_dict(options) + elif isinstance(options, str): + self._logger.info("options is json") + self._settings = SettingsConfigurationOptions.from_json(options) + else: + raise DeepgramError("Invalid options type") + + try: + # speaker substitutes the listening thread + if self._speaker is not None: + self._logger.notice("passing speaker to delegate_listening") + super().delegate_listening(self._speaker) + + # call parent start + if ( + await super().start( + {}, + self._addons, + self._headers, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + is False + ): + self._logger.error("AsyncAgentWebSocketClient.start failed") + self._logger.debug("AsyncAgentWebSocketClient.start LEAVE") + return False + + if self._speaker is not None: + self._logger.notice("speaker is delegate_listening. Starting speaker") + self._speaker.start() + + if self._speaker is not None and self._microphone is not None: + self._logger.notice( + "speaker is delegate_listening. Starting microphone" + ) + self._microphone.set_callback(self.send) + self._microphone.start() + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("after running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + # keepalive thread + if self._config.is_keep_alive_enabled(): + self._logger.notice("keepalive is enabled") + self._keep_alive_thread = asyncio.create_task(self._keep_alive()) + else: + self._logger.notice("keepalive is disabled") + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("after running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + # send the configurationsetting message + self._logger.notice("Sending ConfigurationSettings...") + ret_send_cs = await self.send(str(self._settings)) + if not ret_send_cs: + self._logger.error("ConfigurationSettings failed") + + err_error: ErrorResponse = ErrorResponse( + "Exception in AsyncAgentWebSocketClient.start", + "ConfigurationSettings failed to send", + "Exception", + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Error), + error=err_error, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + + self._logger.debug("AgentWebSocketClient.start LEAVE") + return False + + self._logger.notice("start succeeded") + self._logger.debug("AsyncAgentWebSocketClient.start LEAVE") + return True + + except Exception as e: # pylint: disable=broad-except + self._logger.error( + "WebSocketException in AsyncAgentWebSocketClient.start: %s", e + ) + self._logger.debug("AsyncAgentWebSocketClient.start LEAVE") + if self._config.options.get("termination_exception_connect") is True: + raise e + return False + + # pylint: enable=too-many-branches,too-many-statements + + def on(self, event: AgentWebSocketEvents, handler: Callable) -> None: + """ + Registers event handlers for specific events. + """ + self._logger.info("event subscribed: %s", event) + if event in AgentWebSocketEvents.__members__.values() and callable(handler): + self._event_handlers[event].append(handler) + + async def _emit(self, event: AgentWebSocketEvents, *args, **kwargs) -> None: + """ + Emits events to the registered event handlers. + """ + self._logger.debug("AsyncAgentWebSocketClient._emit ENTER") + self._logger.debug("callback handlers for: %s", event) + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("after running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + self._logger.debug("callback handlers for: %s", event) + tasks = [] + for handler in self._event_handlers[event]: + task = asyncio.create_task(handler(self, *args, **kwargs)) + tasks.append(task) + + if tasks: + self._logger.debug("waiting for tasks to finish...") + await asyncio.gather(*tasks, return_exceptions=True) + tasks.clear() + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("after running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + self._logger.debug("AsyncAgentWebSocketClient._emit LEAVE") + + # pylint: disable=too-many-locals,too-many-statements + async def _process_text(self, message: str) -> None: + """ + Processes messages received over the WebSocket connection. + """ + self._logger.debug("AsyncAgentWebSocketClient._process_text ENTER") + + try: + self._logger.debug("Text data received") + if len(message) == 0: + self._logger.debug("message is empty") + self._logger.debug("AsyncAgentWebSocketClient._process_text LEAVE") + return + + data = json.loads(message) + response_type = data.get("type") + self._logger.debug("response_type: %s, data: %s", response_type, data) + + match response_type: + case AgentWebSocketEvents.Open: + open_result: OpenResponse = OpenResponse.from_json(message) + self._logger.verbose("OpenResponse: %s", open_result) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Open), + open=open_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.Welcome: + welcome_result: WelcomeResponse = WelcomeResponse.from_json(message) + self._logger.verbose("WelcomeResponse: %s", welcome_result) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Welcome), + welcome=welcome_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.SettingsApplied: + settings_applied_result: SettingsAppliedResponse = ( + SettingsAppliedResponse.from_json(message) + ) + self._logger.verbose( + "SettingsAppliedResponse: %s", settings_applied_result + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.SettingsApplied), + settings_applied=settings_applied_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.ConversationText: + conversation_text_result: ConversationTextResponse = ( + ConversationTextResponse.from_json(message) + ) + self._logger.verbose( + "ConversationTextResponse: %s", conversation_text_result + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.ConversationText), + conversation_text=conversation_text_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.UserStartedSpeaking: + user_started_speaking_result: UserStartedSpeakingResponse = ( + UserStartedSpeakingResponse.from_json(message) + ) + self._logger.verbose( + "UserStartedSpeakingResponse: %s", user_started_speaking_result + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.UserStartedSpeaking), + user_started_speaking=user_started_speaking_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.AgentThinking: + agent_thinking_result: AgentThinkingResponse = ( + AgentThinkingResponse.from_json(message) + ) + self._logger.verbose( + "AgentThinkingResponse: %s", agent_thinking_result + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.AgentThinking), + agent_thinking=agent_thinking_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.FunctionCalling: + function_calling_result: FunctionCallingResponse = ( + FunctionCallingResponse.from_json(message) + ) + self._logger.verbose( + "FunctionCallingResponse: %s", function_calling_result + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.FunctionCalling), + function_calling=function_calling_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.AgentStartedSpeaking: + agent_started_speaking_result: AgentStartedSpeakingResponse = ( + AgentStartedSpeakingResponse.from_json(message) + ) + self._logger.verbose( + "AgentStartedSpeakingResponse: %s", + agent_started_speaking_result, + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.AgentStartedSpeaking), + agent_started_speaking=agent_started_speaking_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.AgentAudioDone: + agent_audio_done_result: AgentAudioDoneResponse = ( + AgentAudioDoneResponse.from_json(message) + ) + self._logger.verbose( + "AgentAudioDoneResponse: %s", agent_audio_done_result + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.AgentAudioDone), + agent_audio_done=agent_audio_done_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.Close: + close_result: CloseResponse = CloseResponse.from_json(message) + self._logger.verbose("CloseResponse: %s", close_result) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Close), + close=close_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.Error: + err_error: ErrorResponse = ErrorResponse.from_json(message) + self._logger.verbose("ErrorResponse: %s", err_error) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Error), + error=err_error, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case _: + self._logger.warning( + "Unknown Message: response_type: %s, data: %s", + response_type, + data, + ) + unhandled_error: UnhandledResponse = UnhandledResponse( + type=AgentWebSocketEvents(AgentWebSocketEvents.Unhandled), + raw=message, + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Unhandled), + unhandled=unhandled_error, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + + self._logger.notice("_process_text Succeeded") + self._logger.debug("AsyncAgentWebSocketClient._process_text LEAVE") + + except Exception as e: # pylint: disable=broad-except + self._logger.error( + "Exception in AsyncAgentWebSocketClient._process_text: %s", e + ) + e_error: ErrorResponse = ErrorResponse( + "Exception in AsyncAgentWebSocketClient._process_text", + f"{e}", + "Exception", + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Error), + error=e_error, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + + # signal exit and close + await super()._signal_exit() + + self._logger.debug("AsyncAgentWebSocketClient._process_text LEAVE") + + if self._config.options.get("termination_exception") is True: + raise + return + + # pylint: enable=too-many-locals,too-many-statements + + async def _process_binary(self, message: bytes) -> None: + self._logger.debug("AsyncAgentWebSocketClient._process_binary ENTER") + self._logger.debug("Binary data received") + + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.AudioData), + data=message, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + + self._logger.notice("_process_binary Succeeded") + self._logger.debug("AsyncAgentWebSocketClient._process_binary LEAVE") + + # pylint: disable=too-many-return-statements + async def _keep_alive(self) -> None: + """ + Sends keepalive messages to the WebSocket connection. + """ + self._logger.debug("AsyncAgentWebSocketClient._keep_alive ENTER") + + counter = 0 + while True: + try: + counter += 1 + await asyncio.sleep(ONE_SECOND) + + if self._exit_event.is_set(): + self._logger.notice("_keep_alive exiting gracefully") + self._logger.debug("AsyncAgentWebSocketClient._keep_alive LEAVE") + return + + # deepgram keepalive + if counter % DEEPGRAM_INTERVAL == 0: + await self.keep_alive() + + except Exception as e: # pylint: disable=broad-except + self._logger.error( + "Exception in AsyncAgentWebSocketClient._keep_alive: %s", e + ) + e_error: ErrorResponse = ErrorResponse( + "Exception in AsyncAgentWebSocketClient._keep_alive", + f"{e}", + "Exception", + ) + self._logger.error( + "Exception in AsyncAgentWebSocketClient._keep_alive: %s", str(e) + ) + await self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Error), + error=e_error, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + + # signal exit and close + await super()._signal_exit() + + self._logger.debug("AsyncAgentWebSocketClient._keep_alive LEAVE") + + if self._config.options.get("termination_exception") is True: + raise + return + + async def keep_alive(self) -> bool: + """ + Sends a KeepAlive message + """ + self._logger.spam("AsyncAgentWebSocketClient.keep_alive ENTER") + + self._logger.notice("Sending KeepAlive...") + ret = await self.send(json.dumps({"type": "KeepAlive"})) + + if not ret: + self._logger.error("keep_alive failed") + self._logger.spam("AsyncAgentWebSocketClient.keep_alive LEAVE") + return False + + self._logger.notice("keep_alive succeeded") + self._logger.spam("AsyncAgentWebSocketClient.keep_alive LEAVE") + + return True + + async def _close_message(self) -> bool: + # TODO: No known API close message # pylint: disable=fixme + # return await self.send(json.dumps({"type": "Close"})) + return True + + async def finish(self) -> bool: + """ + Closes the WebSocket connection gracefully. + """ + self._logger.debug("AsyncAgentWebSocketClient.finish ENTER") + + # stop the threads + self._logger.verbose("cancelling tasks...") + try: + # call parent finish + if await super().finish() is False: + self._logger.error("AsyncAgentWebSocketClient.finish failed") + + if self._microphone is not None and self._microphone_created: + self._microphone.finish() + self._microphone_created = False + + if self._speaker is not None and self._speaker_created: + self._speaker.finish() + self._speaker_created = False + + # Before cancelling, check if the tasks were created + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("before running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + tasks = [] + if self._keep_alive_thread is not None: + self._keep_alive_thread.cancel() + tasks.append(self._keep_alive_thread) + self._logger.notice("processing _keep_alive_thread cancel...") + + # Use asyncio.gather to wait for tasks to be cancelled + # Prevent indefinite waiting by setting a timeout + await asyncio.wait_for(asyncio.gather(*tasks), timeout=10) + self._logger.notice("threads joined") + + self._speaker = None + self._microphone = None + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("after running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + self._logger.notice("finish succeeded") + self._logger.spam("AsyncAgentWebSocketClient.finish LEAVE") + return True + + except asyncio.CancelledError as e: + self._logger.error("tasks cancelled error: %s", e) + self._logger.debug("AsyncAgentWebSocketClient.finish LEAVE") + return False + + except asyncio.TimeoutError as e: + self._logger.error("tasks cancellation timed out: %s", e) + self._logger.debug("AsyncAgentWebSocketClient.finish LEAVE") + return False diff --git a/deepgram/clients/agent/v1/websocket/client.py b/deepgram/clients/agent/v1/websocket/client.py new file mode 100644 index 00000000..082449c7 --- /dev/null +++ b/deepgram/clients/agent/v1/websocket/client.py @@ -0,0 +1,654 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +import json +import logging +from typing import Dict, Union, Optional, cast, Any, Callable +import threading +import time + +from .....utils import verboselogs +from .....options import DeepgramClientOptions +from ...enums import AgentWebSocketEvents +from ....common import AbstractSyncWebSocketClient +from ....common import DeepgramError + +from .response import ( + OpenResponse, + WelcomeResponse, + SettingsAppliedResponse, + ConversationTextResponse, + UserStartedSpeakingResponse, + AgentThinkingResponse, + FunctionCallingResponse, + AgentStartedSpeakingResponse, + AgentAudioDoneResponse, + CloseResponse, + ErrorResponse, + UnhandledResponse, +) +from .options import ( + SettingsConfigurationOptions, + UpdateInstructionsOptions, + UpdateSpeakOptions, + InjectAgentMessageOptions, +) + +from .....audio.speaker import ( + Speaker, + RATE as SPEAKER_RATE, + CHANNELS as SPEAKER_CHANNELS, + PLAYBACK_DELTA as SPEAKER_PLAYBACK_DELTA, +) +from .....audio.microphone import ( + Microphone, + RATE as MICROPHONE_RATE, + CHANNELS as MICROPHONE_CHANNELS, +) + +ONE_SECOND = 1 +HALF_SECOND = 0.5 +DEEPGRAM_INTERVAL = 5 + + +class AgentWebSocketClient( + AbstractSyncWebSocketClient +): # pylint: disable=too-many-instance-attributes + """ + Client for interacting with Deepgram's live transcription services over WebSockets. + + This class provides methods to establish a WebSocket connection for live transcription and handle real-time transcription events. + + Args: + config (DeepgramClientOptions): all the options for the client. + """ + + _logger: verboselogs.VerboseLogger + _config: DeepgramClientOptions + _endpoint: str + + _event_handlers: Dict[AgentWebSocketEvents, list] + + _keep_alive_thread: Union[threading.Thread, None] + + _kwargs: Optional[Dict] = None + _addons: Optional[Dict] = None + # note the distinction here. We can't use _config because it's already used in the parent + _settings: Optional[SettingsConfigurationOptions] = None + _headers: Optional[Dict] = None + + _speaker_created: bool = False + _speaker: Optional[Speaker] = None + _microphone_created: bool = False + _microphone: Optional[Microphone] = None + + def __init__(self, config: DeepgramClientOptions): + if config is None: + raise DeepgramError("Config is required") + + self._logger = verboselogs.VerboseLogger(__name__) + self._logger.addHandler(logging.StreamHandler()) + self._logger.setLevel(config.verbose) + + self._config = config + + # needs to be "wss://agent.deepgram.com/agent" + self._endpoint = "agent" + + # override the endpoint since it needs to be "wss://agent.deepgram.com/agent" + self._config.url = "agent.deepgram.com" + + self._keep_alive_thread = None + + # init handlers + self._event_handlers = { + event: [] for event in AgentWebSocketEvents.__members__.values() + } + + if self._config.options.get("microphone_record") == "true": + self._logger.info("microphone_record is enabled") + rate = self._config.options.get("microphone_record_rate", MICROPHONE_RATE) + channels = self._config.options.get( + "microphone_record_channels", MICROPHONE_CHANNELS + ) + device_index = self._config.options.get("microphone_record_device_index") + + self._logger.debug("rate: %s", rate) + self._logger.debug("channels: %s", channels) + + self._microphone_created = True + + if device_index is not None: + self._logger.debug("device_index: %s", device_index) + self._microphone = Microphone( + rate=rate, + channels=channels, + verbose=self._config.verbose, + input_device_index=device_index, + ) + else: + self._microphone = Microphone( + rate=rate, + channels=channels, + verbose=self._config.verbose, + ) + + if self._config.options.get("speaker_playback") == "true": + self._logger.info("speaker_playback is enabled") + rate = self._config.options.get("speaker_playback_rate", SPEAKER_RATE) + channels = self._config.options.get( + "speaker_playback_channels", SPEAKER_CHANNELS + ) + playback_delta_in_ms = self._config.options.get( + "speaker_playback_delta_in_ms", SPEAKER_PLAYBACK_DELTA + ) + device_index = self._config.options.get("speaker_playback_device_index") + + self._logger.debug("rate: %s", rate) + self._logger.debug("channels: %s", channels) + + self._speaker_created = True + + if device_index is not None: + self._logger.debug("device_index: %s", device_index) + + self._speaker = Speaker( + rate=rate, + channels=channels, + last_play_delta_in_ms=playback_delta_in_ms, + verbose=self._config.verbose, + output_device_index=device_index, + microphone=self._microphone, + ) + else: + self._speaker = Speaker( + rate=rate, + channels=channels, + last_play_delta_in_ms=playback_delta_in_ms, + verbose=self._config.verbose, + microphone=self._microphone, + ) + + # call the parent constructor + super().__init__(self._config, self._endpoint) + + # pylint: disable=too-many-statements,too-many-branches + def start( + self, + options: Optional[SettingsConfigurationOptions] = None, + addons: Optional[Dict] = None, + headers: Optional[Dict] = None, + members: Optional[Dict] = None, + **kwargs, + ) -> bool: + """ + Starts the WebSocket connection for agent API. + """ + self._logger.debug("AgentWebSocketClient.start ENTER") + self._logger.info("settings: %s", options) + self._logger.info("addons: %s", addons) + self._logger.info("headers: %s", headers) + self._logger.info("members: %s", members) + self._logger.info("kwargs: %s", kwargs) + + if isinstance(options, SettingsConfigurationOptions) and not options.check(): + self._logger.error("settings.check failed") + self._logger.debug("AgentWebSocketClient.start LEAVE") + raise DeepgramError("Fatal agent settings error") + + self._addons = addons + self._headers = headers + + # add "members" as members of the class + if members is not None: + self.__dict__.update(members) + + # set kwargs as members of the class + if kwargs is not None: + self._kwargs = kwargs + else: + self._kwargs = {} + + if isinstance(options, SettingsConfigurationOptions): + self._logger.info("options is class") + self._settings = options + elif isinstance(options, dict): + self._logger.info("options is dict") + self._settings = SettingsConfigurationOptions.from_dict(options) + elif isinstance(options, str): + self._logger.info("options is json") + self._settings = SettingsConfigurationOptions.from_json(options) + else: + raise DeepgramError("Invalid options type") + + try: + # speaker substitutes the listening thread + if self._speaker is not None: + self._logger.notice("passing speaker to delegate_listening") + super().delegate_listening(self._speaker) + + # call parent start + if ( + super().start( + {}, + self._addons, + self._headers, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + is False + ): + self._logger.error("AgentWebSocketClient.start failed") + self._logger.debug("AgentWebSocketClient.start LEAVE") + return False + + if self._speaker is not None: + self._logger.notice("speaker is delegate_listening. Starting speaker") + self._speaker.start() + + if self._speaker is not None and self._microphone is not None: + self._logger.notice( + "speaker is delegate_listening. Starting microphone" + ) + self._microphone.set_callback(self.send) + self._microphone.start() + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("after running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + # keepalive thread + if self._config.is_keep_alive_enabled(): + self._logger.notice("keepalive is enabled") + self._keep_alive_thread = threading.Thread(target=self._keep_alive) + self._keep_alive_thread.start() + else: + self._logger.notice("keepalive is disabled") + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("after running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + # send the configurationsetting message + self._logger.notice("Sending ConfigurationSettings...") + ret_send_cs = self.send(str(self._settings)) + if not ret_send_cs: + self._logger.error("ConfigurationSettings failed") + + err_error: ErrorResponse = ErrorResponse( + "Exception in AgentWebSocketClient.start", + "ConfigurationSettings failed to send", + "Exception", + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Error), + error=err_error, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + + self._logger.debug("AgentWebSocketClient.start LEAVE") + return False + + self._logger.notice("start succeeded") + self._logger.debug("AgentWebSocketClient.start LEAVE") + return True + + except Exception as e: # pylint: disable=broad-except + self._logger.error( + "WebSocketException in AgentWebSocketClient.start: %s", e + ) + self._logger.debug("AgentWebSocketClient.start LEAVE") + if self._config.options.get("termination_exception_connect") is True: + raise e + return False + + # pylint: enable=too-many-statements,too-many-branches + + def on(self, event: AgentWebSocketEvents, handler: Callable) -> None: + """ + Registers event handlers for specific events. + """ + self._logger.info("event subscribed: %s", event) + if event in AgentWebSocketEvents.__members__.values() and callable(handler): + self._event_handlers[event].append(handler) + + def _emit(self, event: AgentWebSocketEvents, *args, **kwargs) -> None: + """ + Emits events to the registered event handlers. + """ + self._logger.debug("AgentWebSocketClient._emit ENTER") + self._logger.debug("callback handlers for: %s", event) + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("after running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + self._logger.debug("callback handlers for: %s", event) + for handler in self._event_handlers[event]: + handler(self, *args, **kwargs) + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("after running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + self._logger.debug("AgentWebSocketClient._emit LEAVE") + + # pylint: disable=too-many-return-statements,too-many-statements,too-many-locals,too-many-branches + def _process_text(self, message: str) -> None: + """ + Processes messages received over the WebSocket connection. + """ + self._logger.debug("AgentWebSocketClient._process_text ENTER") + + try: + self._logger.debug("Text data received") + if len(message) == 0: + self._logger.debug("message is empty") + self._logger.debug("AgentWebSocketClient._process_text LEAVE") + return + + data = json.loads(message) + response_type = data.get("type") + self._logger.debug("response_type: %s, data: %s", response_type, data) + + match response_type: + case AgentWebSocketEvents.Open: + open_result: OpenResponse = OpenResponse.from_json(message) + self._logger.verbose("OpenResponse: %s", open_result) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Open), + open=open_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.Welcome: + welcome_result: WelcomeResponse = WelcomeResponse.from_json(message) + self._logger.verbose("WelcomeResponse: %s", welcome_result) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Welcome), + welcome=welcome_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.SettingsApplied: + settings_applied_result: SettingsAppliedResponse = ( + SettingsAppliedResponse.from_json(message) + ) + self._logger.verbose( + "SettingsAppliedResponse: %s", settings_applied_result + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.SettingsApplied), + settings_applied=settings_applied_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.ConversationText: + conversation_text_result: ConversationTextResponse = ( + ConversationTextResponse.from_json(message) + ) + self._logger.verbose( + "ConversationTextResponse: %s", conversation_text_result + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.ConversationText), + conversation_text=conversation_text_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.UserStartedSpeaking: + user_started_speaking_result: UserStartedSpeakingResponse = ( + UserStartedSpeakingResponse.from_json(message) + ) + self._logger.verbose( + "UserStartedSpeakingResponse: %s", user_started_speaking_result + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.UserStartedSpeaking), + user_started_speaking=user_started_speaking_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.AgentThinking: + agent_thinking_result: AgentThinkingResponse = ( + AgentThinkingResponse.from_json(message) + ) + self._logger.verbose( + "AgentThinkingResponse: %s", agent_thinking_result + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.AgentThinking), + agent_thinking=agent_thinking_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.FunctionCalling: + function_calling_result: FunctionCallingResponse = ( + FunctionCallingResponse.from_json(message) + ) + self._logger.verbose( + "FunctionCallingResponse: %s", function_calling_result + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.FunctionCalling), + function_calling=function_calling_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.AgentStartedSpeaking: + agent_started_speaking_result: AgentStartedSpeakingResponse = ( + AgentStartedSpeakingResponse.from_json(message) + ) + self._logger.verbose( + "AgentStartedSpeakingResponse: %s", + agent_started_speaking_result, + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.AgentStartedSpeaking), + agent_started_speaking=agent_started_speaking_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.AgentAudioDone: + agent_audio_done_result: AgentAudioDoneResponse = ( + AgentAudioDoneResponse.from_json(message) + ) + self._logger.verbose( + "AgentAudioDoneResponse: %s", agent_audio_done_result + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.AgentAudioDone), + agent_audio_done=agent_audio_done_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.Close: + close_result: CloseResponse = CloseResponse.from_json(message) + self._logger.verbose("CloseResponse: %s", close_result) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Close), + close=close_result, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case AgentWebSocketEvents.Error: + err_error: ErrorResponse = ErrorResponse.from_json(message) + self._logger.verbose("ErrorResponse: %s", err_error) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Error), + error=err_error, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + case _: + self._logger.warning( + "Unknown Message: response_type: %s, data: %s", + response_type, + data, + ) + unhandled_error: UnhandledResponse = UnhandledResponse( + type=AgentWebSocketEvents(AgentWebSocketEvents.Unhandled), + raw=message, + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Unhandled), + unhandled=unhandled_error, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + + self._logger.notice("_process_text Succeeded") + self._logger.debug("SpeakStreamClient._process_text LEAVE") + + except Exception as e: # pylint: disable=broad-except + self._logger.error("Exception in AgentWebSocketClient._process_text: %s", e) + e_error: ErrorResponse = ErrorResponse( + "Exception in AgentWebSocketClient._process_text", + f"{e}", + "Exception", + ) + self._logger.error( + "Exception in AgentWebSocketClient._process_text: %s", str(e) + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Error), + error=e_error, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + + # signal exit and close + super()._signal_exit() + + self._logger.debug("AgentWebSocketClient._process_text LEAVE") + + if self._config.options.get("termination_exception") is True: + raise + return + + # pylint: enable=too-many-return-statements,too-many-statements + + def _process_binary(self, message: bytes) -> None: + self._logger.debug("AgentWebSocketClient._process_binary ENTER") + self._logger.debug("Binary data received") + + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.AudioData), + data=message, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + + self._logger.notice("_process_binary Succeeded") + self._logger.debug("AgentWebSocketClient._process_binary LEAVE") + + # pylint: disable=too-many-return-statements + def _keep_alive(self) -> None: + """ + Sends keepalive messages to the WebSocket connection. + """ + self._logger.debug("AgentWebSocketClient._keep_alive ENTER") + + counter = 0 + while True: + try: + counter += 1 + self._exit_event.wait(timeout=ONE_SECOND) + + if self._exit_event.is_set(): + self._logger.notice("_keep_alive exiting gracefully") + self._logger.debug("AgentWebSocketClient._keep_alive LEAVE") + return + + # deepgram keepalive + if counter % DEEPGRAM_INTERVAL == 0: + self.keep_alive() + + except Exception as e: # pylint: disable=broad-except + self._logger.error( + "Exception in AgentWebSocketClient._keep_alive: %s", e + ) + e_error: ErrorResponse = ErrorResponse( + "Exception in AgentWebSocketClient._keep_alive", + f"{e}", + "Exception", + ) + self._logger.error( + "Exception in AgentWebSocketClient._keep_alive: %s", str(e) + ) + self._emit( + AgentWebSocketEvents(AgentWebSocketEvents.Error), + error=e_error, + **dict(cast(Dict[Any, Any], self._kwargs)), + ) + + # signal exit and close + super()._signal_exit() + + self._logger.debug("AgentWebSocketClient._keep_alive LEAVE") + + if self._config.options.get("termination_exception") is True: + raise + return + + def keep_alive(self) -> bool: + """ + Sends a KeepAlive message + """ + self._logger.spam("AgentWebSocketClient.keep_alive ENTER") + + self._logger.notice("Sending KeepAlive...") + ret = self.send(json.dumps({"type": "KeepAlive"})) + + if not ret: + self._logger.error("keep_alive failed") + self._logger.spam("AgentWebSocketClient.keep_alive LEAVE") + return False + + self._logger.notice("keep_alive succeeded") + self._logger.spam("AgentWebSocketClient.keep_alive LEAVE") + + return True + + def _close_message(self) -> bool: + # TODO: No known API close message # pylint: disable=fixme + # return self.send(json.dumps({"type": "Close"})) + return True + + # closes the WebSocket connection gracefully + def finish(self) -> bool: + """ + Closes the WebSocket connection gracefully. + """ + self._logger.spam("AgentWebSocketClient.finish ENTER") + + # call parent finish + if super().finish() is False: + self._logger.error("AgentWebSocketClient.finish failed") + + if self._microphone is not None and self._microphone_created: + self._microphone.finish() + self._microphone_created = False + + if self._speaker is not None and self._speaker_created: + self._speaker.finish() + self._speaker_created = False + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("before running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + # stop the threads + self._logger.verbose("cancelling tasks...") + if self._keep_alive_thread is not None: + self._keep_alive_thread.join() + self._keep_alive_thread = None + self._logger.notice("processing _keep_alive_thread thread joined") + + if self._listen_thread is not None: + self._listen_thread.join() + self._listen_thread = None + self._logger.notice("listening thread joined") + + self._speaker = None + self._microphone = None + + # debug the threads + for thread in threading.enumerate(): + self._logger.debug("before running thread: %s", thread.name) + self._logger.debug("number of active threads: %s", threading.active_count()) + + self._logger.notice("finish succeeded") + self._logger.spam("AgentWebSocketClient.finish LEAVE") + return True diff --git a/deepgram/clients/agent/v1/websocket/options.py b/deepgram/clients/agent/v1/websocket/options.py new file mode 100644 index 00000000..ad9b77a6 --- /dev/null +++ b/deepgram/clients/agent/v1/websocket/options.py @@ -0,0 +1,319 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +from typing import List, Optional, Union, Any, Tuple +import logging + +from dataclasses import dataclass, field +from dataclasses_json import config as dataclass_config + +from deepgram.utils import verboselogs + +from ...enums import AgentWebSocketEvents +from ....common import BaseResponse + + +# ConfigurationSettings + + +@dataclass +class Listen(BaseResponse): + """ + This class defines any configuration settings for the Listen model. + """ + + model: Optional[str] = field(default="nova-2") + + +@dataclass +class Speak(BaseResponse): + """ + This class defines any configuration settings for the Speak model. + """ + + model: Optional[str] = field(default="aura-asteria-en") + + +@dataclass +class Header(BaseResponse): + """ + This class defines a single key/value pair for a header. + """ + + key: str + value: str + + +@dataclass +class Item(BaseResponse): + """ + This class defines a single item in a list of items. + """ + + type: str + description: str + + +@dataclass +class Properties(BaseResponse): + """ + This class defines the properties which is just a list of items. + """ + + item: Item + + def __getitem__(self, key): + _dict = self.to_dict() + if "item" in _dict: + _dict["item"] = [Item.from_dict(item) for item in _dict["item"]] + return _dict[key] + + +@dataclass +class Parameters(BaseResponse): + """ + This class defines the parameters for a function. + """ + + type: str + properties: Properties + required: List[str] + + def __getitem__(self, key): + _dict = self.to_dict() + if "properties" in _dict: + _dict["properties"] = _dict["properties"].copy() + return _dict[key] + + +@dataclass +class Function(BaseResponse): + """ + This class defines a function for the Think model. + """ + + name: str + description: str + url: str + method: str + headers: Optional[List[Header]] = field( + default=None, metadata=dataclass_config(exclude=lambda f: f is None) + ) + parameters: Optional[Parameters] = field( + default=None, metadata=dataclass_config(exclude=lambda f: f is None) + ) + + def __getitem__(self, key): + _dict = self.to_dict() + if "parameters" in _dict: + _dict["parameters"] = [ + Parameters.from_dict(parameters) for parameters in _dict["parameters"] + ] + if "headers" in _dict: + _dict["headers"] = [ + Header.from_dict(headers) for headers in _dict["headers"] + ] + return _dict[key] + + +@dataclass +class Provider(BaseResponse): + """ + This class defines the provider for the Think model. + """ + + type: Optional[str] = field( + default=None, metadata=dataclass_config(exclude=lambda f: f is None) + ) + + +@dataclass +class Think(BaseResponse): + """ + This class defines any configuration settings for the Think model. + """ + + provider: Provider = field(default=Provider()) + model: Optional[str] = field( + default=None, metadata=dataclass_config(exclude=lambda f: f is None) + ) + instructions: Optional[str] = field( + default=None, metadata=dataclass_config(exclude=lambda f: f is None) + ) + functions: Optional[List[Function]] = field( + default=None, metadata=dataclass_config(exclude=lambda f: f is None) + ) + + def __getitem__(self, key): + _dict = self.to_dict() + if "provider" in _dict: + _dict["provider"] = [ + Provider.from_dict(provider) for provider in _dict["provider"] + ] + if "functions" in _dict: + _dict["functions"] = [ + Function.from_dict(functions) for functions in _dict["functions"] + ] + return _dict[key] + + +@dataclass +class Agent(BaseResponse): + """ + This class defines any configuration settings for the Agent model. + """ + + listen: Listen = field(default=Listen()) + think: Think = field(default=Think()) + speak: Speak = field(default=Speak()) + + def __getitem__(self, key): + _dict = self.to_dict() + if "listen" in _dict: + _dict["listen"] = [Listen.from_dict(listen) for listen in _dict["listen"]] + if "think" in _dict: + _dict["think"] = [Think.from_dict(think) for think in _dict["think"]] + if "speak" in _dict: + _dict["speak"] = [Speak.from_dict(speak) for speak in _dict["speak"]] + return _dict[key] + + +@dataclass +class Input(BaseResponse): + """ + This class defines any configuration settings for the input audio. + """ + + encoding: Optional[str] = field(default="linear16") + sample_rate: int = field(default=16000) + + +@dataclass +class Output(BaseResponse): + """ + This class defines any configuration settings for the output audio. + """ + + encoding: Optional[str] = field(default="linear16") + sample_rate: Optional[int] = field(default=16000) + bitrate: Optional[int] = field( + default=None, metadata=dataclass_config(exclude=lambda f: f is None) + ) + container: Optional[str] = field(default="none") + + +@dataclass +class Audio(BaseResponse): + """ + This class defines any configuration settings for the audio. + """ + + input: Optional[Input] = field(default=Input()) + output: Optional[Output] = field(default=Output()) + + def __getitem__(self, key): + _dict = self.to_dict() + if "input" in _dict: + _dict["input"] = [Input.from_dict(input) for input in _dict["input"]] + if "output" in _dict: + _dict["output"] = [Output.from_dict(output) for output in _dict["output"]] + return _dict[key] + + +@dataclass +class Context(BaseResponse): + """ + This class defines any configuration settings for the context. + """ + + messages: Optional[List[Tuple[str, str]]] = field( + default=None, metadata=dataclass_config(exclude=lambda f: f is None) + ) + replay: Optional[bool] = field(default=False) + + def __getitem__(self, key): + _dict = self.to_dict() + if "messages" in _dict: + _dict["messages"] = _dict["messages"].copy() + return _dict[key] + + +@dataclass +class SettingsConfigurationOptions(BaseResponse): + """ + The client should send a SettingsConfiguration message immediately after opening the websocket and before sending any audio. + """ + + type: str = str(AgentWebSocketEvents.SettingsConfiguration) + audio: Audio = field(default=Audio()) + agent: Agent = field(default=Agent()) + context: Optional[Context] = field( + default=None, metadata=dataclass_config(exclude=lambda f: f is None) + ) + + def __getitem__(self, key): + _dict = self.to_dict() + if "audio" in _dict: + _dict["audio"] = [Audio.from_dict(audio) for audio in _dict["audio"]] + if "agent" in _dict: + _dict["agent"] = [Agent.from_dict(agent) for agent in _dict["agent"]] + if "context" in _dict: + _dict["context"] = [ + Context.from_dict(context) for context in _dict["context"] + ] + return _dict[key] + + def check(self): + """ + Check the options for any deprecated or soon-to-be-deprecated options. + """ + logger = verboselogs.VerboseLogger(__name__) + logger.addHandler(logging.StreamHandler()) + prev = logger.level + logger.setLevel(verboselogs.ERROR) + + # do we need to check anything here? + + logger.setLevel(prev) + + return True + + +# UpdateInstructions + + +@dataclass +class UpdateInstructionsOptions(BaseResponse): + """ + The client can send an UpdateInstructions message to give additional instructions to the Think model in the middle of a conversation. + """ + + type: str = str(AgentWebSocketEvents.UpdateInstructions) + instructions: str = field(default="") + + +# UpdateSpeak + + +@dataclass +class UpdateSpeakOptions(BaseResponse): + """ + The client can send an UpdateSpeak message to change the Speak model in the middle of a conversation. + """ + + type: str = str(AgentWebSocketEvents.UpdateSpeak) + model: str = field(default="") + + +# InjectAgentMessage + + +@dataclass +class InjectAgentMessageOptions(BaseResponse): + """ + The client can send an InjectAgentMessage to immediately trigger an agent statement. If the injection request arrives while the user is speaking, or while the server is in the middle of sending audio for an agent response, then the request will be ignored and the server will reply with an InjectionRefused. + """ + + type: str = str(AgentWebSocketEvents.InjectAgentMessage) + message: str = field(default="") diff --git a/deepgram/clients/agent/v1/websocket/response.py b/deepgram/clients/agent/v1/websocket/response.py new file mode 100644 index 00000000..6d1d69b3 --- /dev/null +++ b/deepgram/clients/agent/v1/websocket/response.py @@ -0,0 +1,96 @@ +# Copyright 2023-2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +from typing import List, Optional, Dict, Any + +from dataclasses import dataclass + +# common websocket response +from ....common import ( + BaseResponse, + OpenResponse, + CloseResponse, + ErrorResponse, + UnhandledResponse, +) + +# unique + + +@dataclass +class WelcomeResponse(BaseResponse): + """ + The server will send a Welcome message as soon as the websocket opens. + """ + + type: str + session_id: str + + +@dataclass +class SettingsAppliedResponse(BaseResponse): + """ + The server will send a SettingsApplied message as soon as the settings are applied. + """ + + type: str + + +@dataclass +class ConversationTextResponse(BaseResponse): + """ + The server will send a ConversationText message every time the agent hears the user say something, and every time the agent speaks something itself. + """ + + type: str + role: str + content: str + + +@dataclass +class UserStartedSpeakingResponse(BaseResponse): + """ + The server will send a UserStartedSpeaking message every time the user begins a new utterance. + """ + + type: str + + +@dataclass +class AgentThinkingResponse(BaseResponse): + """ + The server will send an AgentThinking message to inform the client of a non-verbalized agent thought. + """ + + type: str + content: str + + +@dataclass +class FunctionCallingResponse(BaseResponse): + """ + The server will sometimes send FunctionCalling messages when making function calls to help the client developer debug function calling workflows. + """ + + type: str + + +@dataclass +class AgentStartedSpeakingResponse(BaseResponse): + """ + The server will send an AgentStartedSpeaking message when it begins streaming an agent audio response to the client for playback. + """ + + total_latency: float + tts_latency: float + ttt_latency: float + + +@dataclass +class AgentAudioDoneResponse(BaseResponse): + """ + The server will send an AgentAudioDone message immediately after it sends the last audio message in a piece of agent speech. + """ + + type: str diff --git a/deepgram/clients/agent_router.py b/deepgram/clients/agent_router.py new file mode 100644 index 00000000..90d82f3a --- /dev/null +++ b/deepgram/clients/agent_router.py @@ -0,0 +1,130 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +from importlib import import_module +import logging + +from ..utils import verboselogs +from ..options import DeepgramClientOptions +from .errors import DeepgramModuleError + + +class AgentRouter: + """ + Represents a client for interacting with the Deepgram API. + + This class provides a client for making requests to the Deepgram API with various configuration options. + + Attributes: + config_options (DeepgramClientOptions): An optional configuration object specifying client options. + + Raises: + DeepgramApiKeyError: If the API key is missing or invalid. + + Methods: + read: (Preferred) Returns an Threaded AnalyzeClient instance for interacting with Deepgram's read transcription services. + asyncread: Returns an (Async) AnalyzeClient instance for interacting with Deepgram's read transcription services. + """ + + _logger: verboselogs.VerboseLogger + _config: DeepgramClientOptions + + def __init__(self, config: DeepgramClientOptions): + self._logger = verboselogs.VerboseLogger(__name__) + self._logger.addHandler(logging.StreamHandler()) + self._logger.setLevel(config.verbose) + self._config = config + + @property + def websocket(self): + """ + Returns an AgentWebSocketClient instance for interacting with Deepgram's Agent API. + """ + return self.Version(self._config, "websocket") + + @property + def asyncwebsocket(self): + """ + Returns an AsyncAgentWebSocketClient instance for interacting with Deepgram's Agent API. + """ + return self.Version(self._config, "asyncwebsocket") + + # INTERNAL CLASSES + class Version: + """ + Represents a version of the Deepgram API. + """ + + _logger: verboselogs.VerboseLogger + _config: DeepgramClientOptions + _parent: str + + def __init__(self, config, parent: str): + self._logger = verboselogs.VerboseLogger(__name__) + self._logger.addHandler(logging.StreamHandler()) + self._logger.setLevel(config.verbose) + self._config = config + self._parent = parent + + # FUTURE VERSIONING: + # When v2 or v1.1beta1 or etc. This allows easy access to the latest version of the API. + # @property + # def latest(self): + # match self._parent: + # case "analyze": + # return AnalyzeClient(self._config) + # case _: + # raise DeepgramModuleError("Invalid parent") + + def v(self, version: str = ""): + """ + Returns a specific version of the Deepgram API. + """ + self._logger.debug("Version.v ENTER") + self._logger.info("version: %s", version) + if len(version) == 0: + self._logger.error("version is empty") + self._logger.debug("Version.v LEAVE") + raise DeepgramModuleError("Invalid module version") + + parent = "" + file_name = "" + class_name = "" + match self._parent: + case "websocket": + parent = "websocket" + file_name = "client" + class_name = "AgentWebSocketClient" + case "asyncwebsocket": + parent = "websocket" + file_name = "async_client" + class_name = "AsyncAgentWebSocketClient" + case _: + self._logger.error("parent unknown: %s", self._parent) + self._logger.debug("Version.v LEAVE") + raise DeepgramModuleError("Invalid parent type") + + # create class path + path = f"deepgram.clients.agent.v{version}.{parent}.{file_name}" + self._logger.info("path: %s", path) + self._logger.info("class_name: %s", class_name) + + # import class + mod = import_module(path) + if mod is None: + self._logger.error("module path is None") + self._logger.debug("Version.v LEAVE") + raise DeepgramModuleError("Unable to find package") + + my_class = getattr(mod, class_name) + if my_class is None: + self._logger.error("my_class is None") + self._logger.debug("Version.v LEAVE") + raise DeepgramModuleError("Unable to find class") + + # instantiate class + my_class = my_class(self._config) + self._logger.notice("Version.v succeeded") + self._logger.debug("Version.v LEAVE") + return my_class diff --git a/deepgram/clients/common/v1/abstract_async_rest.py b/deepgram/clients/common/v1/abstract_async_rest.py index 1a3be9cd..3c0d8056 100644 --- a/deepgram/clients/common/v1/abstract_async_rest.py +++ b/deepgram/clients/common/v1/abstract_async_rest.py @@ -229,6 +229,13 @@ async def _handle_request( method, _url, headers=_headers, **kwargs ) response.raise_for_status() + + # throw exception if response is None or response.text is None + if response is None or response.text is None: + raise DeepgramError( + "Response is not available yet. Please try again later." + ) + return response.text except httpx.HTTPError as e1: diff --git a/deepgram/clients/common/v1/abstract_async_websocket.py b/deepgram/clients/common/v1/abstract_async_websocket.py index 8a35a98d..c6a75193 100644 --- a/deepgram/clients/common/v1/abstract_async_websocket.py +++ b/deepgram/clients/common/v1/abstract_async_websocket.py @@ -84,7 +84,7 @@ def delegate_listening(self, delegate: Speaker) -> None: # pylint: disable=too-many-branches,too-many-statements async def start( self, - options: Optional[Dict] = None, + options: Optional[Any] = None, addons: Optional[Dict] = None, headers: Optional[Dict] = None, **kwargs, @@ -106,6 +106,11 @@ async def start( else: self._kwargs = {} + if not isinstance(options, dict): + self._logger.error("options is not a dict") + self._logger.debug("AbstractSyncWebSocketClient.start LEAVE") + return False + # set options if options is not None: self._options = options @@ -448,7 +453,10 @@ async def finish(self) -> bool: # debug the threads for thread in threading.enumerate(): - self._logger.debug("after running thread: %s", thread.name) + if thread is not None and thread.name is not None: + self._logger.debug("after running thread: %s", thread.name) + else: + self._logger.debug("after running thread: unknown_thread_name") self._logger.debug("number of active threads: %s", threading.active_count()) self._logger.notice("finish succeeded") diff --git a/deepgram/clients/common/v1/abstract_sync_rest.py b/deepgram/clients/common/v1/abstract_sync_rest.py index 5aba233f..cbfd40ef 100644 --- a/deepgram/clients/common/v1/abstract_sync_rest.py +++ b/deepgram/clients/common/v1/abstract_sync_rest.py @@ -225,6 +225,13 @@ def _handle_request( kwargs.pop("transport") response = client.request(method, _url, headers=_headers, **kwargs) response.raise_for_status() + + # throw exception if response is None or response.text is None + if response is None or response.text is None: + raise DeepgramError( + "Response is not available yet. Please try again later." + ) + return response.text except httpx.HTTPError as e1: diff --git a/deepgram/clients/common/v1/abstract_sync_websocket.py b/deepgram/clients/common/v1/abstract_sync_websocket.py index b29e9e1d..4e796a90 100644 --- a/deepgram/clients/common/v1/abstract_sync_websocket.py +++ b/deepgram/clients/common/v1/abstract_sync_websocket.py @@ -88,7 +88,7 @@ def delegate_listening(self, delegate: Speaker) -> None: # pylint: disable=too-many-statements,too-many-branches def start( self, - options: Optional[Dict] = None, + options: Optional[Any] = None, addons: Optional[Dict] = None, headers: Optional[Dict] = None, **kwargs, @@ -110,6 +110,11 @@ def start( else: self._kwargs = {} + if not isinstance(options, dict): + self._logger.error("options is not a dict") + self._logger.debug("AbstractSyncWebSocketClient.start LEAVE") + return False + # set options if options is not None: self._options = options @@ -445,7 +450,10 @@ def finish(self) -> bool: # debug the threads for thread in threading.enumerate(): - self._logger.debug("before running thread: %s", thread.name) + if thread is not None and thread.name is not None: + self._logger.debug("before running thread: %s", thread.name) + else: + self._logger.debug("after running thread: unknown_thread_name") self._logger.debug("number of active threads: %s", threading.active_count()) self._logger.notice("finish succeeded") diff --git a/deepgram/clients/listen/v1/helpers.py b/deepgram/clients/listen/v1/helpers.py deleted file mode 100644 index c7429acd..00000000 --- a/deepgram/clients/listen/v1/helpers.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2023-2024 Deepgram SDK contributors. All Rights Reserved. -# Use of this source code is governed by a MIT license that can be found in the LICENSE file. -# SPDX-License-Identifier: MIT - -from urllib.parse import urlparse, urlunparse, parse_qs, urlencode -from typing import Dict, Optional -import re - - -# This function appends query parameters to a URL -def append_query_params(url: str, params: Optional[Dict] = None): - """ - Appends query parameters to a URL - """ - parsed_url = urlparse(url) - query_params = parse_qs(parsed_url.query) - - if params is not None: - for key, value in params.items(): - if value is None: - continue - if isinstance(value, bool): - value = str(value).lower() - if isinstance(value, list): - for item in value: - query_params[key] = query_params.get(key, []) + [str(item)] - else: - query_params[key] = [str(value)] - - updated_query_string = urlencode(query_params, doseq=True) - updated_url = parsed_url._replace(query=updated_query_string).geturl() - return updated_url - - -# This function converts a URL to a WebSocket URL -def convert_to_websocket_url(base_url: str, endpoint: str): - """ - Converts a URL to a WebSocket URL - """ - use_ssl = True # Default to true - if re.match(r"^https?://", base_url, re.IGNORECASE): - if "http://" in base_url: - use_ssl = False # Override to false if http:// is found - base_url = base_url.replace("https://", "").replace("http://", "") - if not re.match(r"^wss?://", base_url, re.IGNORECASE): - if use_ssl: - base_url = "wss://" + base_url - else: - base_url = "ws://" + base_url - parsed_url = urlparse(base_url) - domain = parsed_url.netloc - websocket_url = urlunparse((parsed_url.scheme, domain, endpoint, "", "", "")) - return websocket_url diff --git a/deepgram/clients/listen_router.py b/deepgram/clients/listen_router.py index 20b35261..7bc88bfe 100644 --- a/deepgram/clients/listen_router.py +++ b/deepgram/clients/listen_router.py @@ -18,14 +18,13 @@ from .errors import DeepgramModuleError -class Listen: +class ListenRouter: """ Represents a client for interacting with the Deepgram API. This class provides a client for making requests to the Deepgram API with various configuration options. Attributes: - api_key (str): The Deepgram API key used for authentication. config_options (DeepgramClientOptions): An optional configuration object specifying client options. Raises: diff --git a/deepgram/clients/read_router.py b/deepgram/clients/read_router.py index 9ce4c81c..1ca09ee3 100644 --- a/deepgram/clients/read_router.py +++ b/deepgram/clients/read_router.py @@ -10,14 +10,13 @@ from .errors import DeepgramModuleError -class Read: +class ReadRouter: """ Represents a client for interacting with the Deepgram API. This class provides a client for making requests to the Deepgram API with various configuration options. Attributes: - api_key (str): The Deepgram API key used for authentication. config_options (DeepgramClientOptions): An optional configuration object specifying client options. Raises: @@ -40,14 +39,14 @@ def __init__(self, config: DeepgramClientOptions): @property def analyze(self): """ - Returns an AnalyzeClient instance for interacting with Deepgram's read transcription services. + Returns an AnalyzeClient instance for interacting with Deepgram's read services. """ return self.Version(self._config, "analyze") @property def asyncanalyze(self): """ - Returns an AsyncAnalyzeClient instance for interacting with Deepgram's read transcription services. + Returns an AsyncAnalyzeClient instance for interacting with Deepgram's read services. """ return self.Version(self._config, "asyncanalyze") diff --git a/deepgram/clients/speak/v1/websocket/async_client.py b/deepgram/clients/speak/v1/websocket/async_client.py index c5c1cb16..b7965c69 100644 --- a/deepgram/clients/speak/v1/websocket/async_client.py +++ b/deepgram/clients/speak/v1/websocket/async_client.py @@ -27,6 +27,7 @@ ) from .options import SpeakWSOptions +from .....audio.microphone import Microphone from .....audio.speaker import Speaker, RATE, CHANNELS, PLAYBACK_DELTA ONE_SECOND = 1 @@ -62,9 +63,13 @@ class AsyncSpeakWSClient( _options: Optional[Dict] = None _headers: Optional[Dict] = None + _speaker_created: bool = False _speaker: Optional[Speaker] = None + _microphone: Optional[Microphone] = None - def __init__(self, config: DeepgramClientOptions): + def __init__( + self, config: DeepgramClientOptions, microphone: Optional[Microphone] = None + ): if config is None: raise DeepgramError("Config is required") self._logger = verboselogs.VerboseLogger(__name__) @@ -80,6 +85,9 @@ def __init__(self, config: DeepgramClientOptions): self._last_datagram = None self._flush_count = 0 + # microphone + self._microphone = microphone + # init handlers self._event_handlers = { event: [] for event in SpeakWebSocketEvents.__members__.values() @@ -104,6 +112,8 @@ def __init__(self, config: DeepgramClientOptions): self._logger.debug("channels: %s", channels) self._logger.debug("device_index: %s", device_index) + self._speaker_created = True + if device_index is not None: self._speaker = Speaker( rate=rate, @@ -111,6 +121,7 @@ def __init__(self, config: DeepgramClientOptions): last_play_delta_in_ms=playback_delta_in_ms, verbose=self._config.verbose, output_device_index=device_index, + microphone=self._microphone, ) else: self._speaker = Speaker( @@ -118,6 +129,7 @@ def __init__(self, config: DeepgramClientOptions): channels=channels, last_play_delta_in_ms=playback_delta_in_ms, verbose=self._config.verbose, + microphone=self._microphone, ) # call the parent constructor @@ -628,6 +640,10 @@ async def finish(self) -> bool: if await super().finish() is False: self._logger.error("AsyncListenWebSocketClient.finish failed") + if self._speaker is not None and self._speaker_created: + self._speaker.finish() + self._speaker_created = False + # Before cancelling, check if the tasks were created # debug the threads for thread in threading.enumerate(): diff --git a/deepgram/clients/speak/v1/websocket/client.py b/deepgram/clients/speak/v1/websocket/client.py index f334bb9b..05580bfa 100644 --- a/deepgram/clients/speak/v1/websocket/client.py +++ b/deepgram/clients/speak/v1/websocket/client.py @@ -27,6 +27,7 @@ ) from .options import SpeakWSOptions +from .....audio.microphone import Microphone from .....audio.speaker import Speaker, RATE, CHANNELS, PLAYBACK_DELTA ONE_SECOND = 1 @@ -63,9 +64,13 @@ class SpeakWSClient( _options: Optional[Dict] = None _headers: Optional[Dict] = None + _speaker_created: bool = False _speaker: Optional[Speaker] = None + _microphone: Optional[Microphone] = None - def __init__(self, config: DeepgramClientOptions): + def __init__( + self, config: DeepgramClientOptions, microphone: Optional[Microphone] = None + ): if config is None: raise DeepgramError("Config is required") @@ -83,6 +88,9 @@ def __init__(self, config: DeepgramClientOptions): self._last_datagram = None self._flush_count = 0 + # microphone + self._microphone = microphone + # init handlers self._event_handlers = { event: [] for event in SpeakWebSocketEvents.__members__.values() @@ -107,6 +115,8 @@ def __init__(self, config: DeepgramClientOptions): self._logger.debug("channels: %s", channels) self._logger.debug("device_index: %s", device_index) + self._speaker_created = True + if device_index is not None: self._speaker = Speaker( rate=rate, @@ -114,6 +124,7 @@ def __init__(self, config: DeepgramClientOptions): last_play_delta_in_ms=playback_delta_in_ms, verbose=self._config.verbose, output_device_index=device_index, + microphone=self._microphone, ) else: self._speaker = Speaker( @@ -121,6 +132,7 @@ def __init__(self, config: DeepgramClientOptions): channels=channels, last_play_delta_in_ms=playback_delta_in_ms, verbose=self._config.verbose, + microphone=self._microphone, ) # call the parent constructor @@ -624,6 +636,10 @@ def finish(self) -> bool: if super().finish() is False: self._logger.error("ListenWebSocketClient.finish failed") + if self._speaker is not None and self._speaker_created: + self._speaker.finish() + self._speaker_created = False + # debug the threads for thread in threading.enumerate(): self._logger.debug("before running thread: %s", thread.name) diff --git a/deepgram/clients/speak/v1/websocket/helpers.py b/deepgram/clients/speak/v1/websocket/helpers.py deleted file mode 100644 index c7429acd..00000000 --- a/deepgram/clients/speak/v1/websocket/helpers.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2023-2024 Deepgram SDK contributors. All Rights Reserved. -# Use of this source code is governed by a MIT license that can be found in the LICENSE file. -# SPDX-License-Identifier: MIT - -from urllib.parse import urlparse, urlunparse, parse_qs, urlencode -from typing import Dict, Optional -import re - - -# This function appends query parameters to a URL -def append_query_params(url: str, params: Optional[Dict] = None): - """ - Appends query parameters to a URL - """ - parsed_url = urlparse(url) - query_params = parse_qs(parsed_url.query) - - if params is not None: - for key, value in params.items(): - if value is None: - continue - if isinstance(value, bool): - value = str(value).lower() - if isinstance(value, list): - for item in value: - query_params[key] = query_params.get(key, []) + [str(item)] - else: - query_params[key] = [str(value)] - - updated_query_string = urlencode(query_params, doseq=True) - updated_url = parsed_url._replace(query=updated_query_string).geturl() - return updated_url - - -# This function converts a URL to a WebSocket URL -def convert_to_websocket_url(base_url: str, endpoint: str): - """ - Converts a URL to a WebSocket URL - """ - use_ssl = True # Default to true - if re.match(r"^https?://", base_url, re.IGNORECASE): - if "http://" in base_url: - use_ssl = False # Override to false if http:// is found - base_url = base_url.replace("https://", "").replace("http://", "") - if not re.match(r"^wss?://", base_url, re.IGNORECASE): - if use_ssl: - base_url = "wss://" + base_url - else: - base_url = "ws://" + base_url - parsed_url = urlparse(base_url) - domain = parsed_url.netloc - websocket_url = urlunparse((parsed_url.scheme, domain, endpoint, "", "", "")) - return websocket_url diff --git a/deepgram/clients/speak_router.py b/deepgram/clients/speak_router.py index 5e5dec2f..7290ed48 100644 --- a/deepgram/clients/speak_router.py +++ b/deepgram/clients/speak_router.py @@ -13,7 +13,7 @@ from .errors import DeepgramModuleError -class Speak: +class SpeakRouter: """ This class provides a Speak Clients for making requests to the Deepgram API with various configuration options. diff --git a/examples/agent/async_simple/main.py b/examples/agent/async_simple/main.py new file mode 100644 index 00000000..3c04b090 --- /dev/null +++ b/examples/agent/async_simple/main.py @@ -0,0 +1,152 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +from signal import SIGINT, SIGTERM +import asyncio +import time +from deepgram.utils import verboselogs + +from deepgram import ( + DeepgramClient, + DeepgramClientOptions, + AgentWebSocketEvents, + SettingsConfigurationOptions, +) + +TTS_TEXT = "Hello, this is a text to speech example using Deepgram." + +global warning_notice +warning_notice = True + + +async def main(): + try: + loop = asyncio.get_event_loop() + + for signal in (SIGTERM, SIGINT): + loop.add_signal_handler( + signal, + lambda: asyncio.create_task(shutdown(signal, loop, dg_connection)), + ) + + # example of setting up a client config. logging values: WARNING, VERBOSE, DEBUG, SPAM + config: DeepgramClientOptions = DeepgramClientOptions( + options={ + "keepalive": "true", + "microphone_record": "true", + "speaker_playback": "true", + }, + # verbose=verboselogs.DEBUG, + ) + deepgram: DeepgramClient = DeepgramClient("", config) + + # Create a websocket connection to Deepgram + dg_connection = deepgram.agent.asyncwebsocket.v("1") + + async def on_open(self, open, **kwargs): + print(f"\n\n{open}\n\n") + + async def on_binary_data(self, data, **kwargs): + global warning_notice + if warning_notice: + print("Received binary data") + print("You can do something with the binary data here") + print("OR") + print( + "If you want to simply play the audio, set speaker_playback to true in the options for DeepgramClientOptions" + ) + warning_notice = False + + async def on_welcome(self, welcome, **kwargs): + print(f"\n\n{welcome}\n\n") + + async def on_settings_applied(self, settings_applied, **kwargs): + print(f"\n\n{settings_applied}\n\n") + + async def on_conversation_text(self, conversation_text, **kwargs): + print(f"\n\n{conversation_text}\n\n") + + async def on_user_started_speaking(self, user_started_speaking, **kwargs): + print(f"\n\n{user_started_speaking}\n\n") + + async def on_agent_thinking(self, agent_thinking, **kwargs): + print(f"\n\n{agent_thinking}\n\n") + + async def on_function_calling(self, function_calling, **kwargs): + print(f"\n\n{function_calling}\n\n") + + async def on_agent_started_speaking(self, agent_started_speaking, **kwargs): + print(f"\n\n{agent_started_speaking}\n\n") + + async def on_agent_audio_done(self, agent_audio_done, **kwargs): + print(f"\n\n{agent_audio_done}\n\n") + + async def on_close(self, close, **kwargs): + print(f"\n\n{close}\n\n") + + async def on_error(self, error, **kwargs): + print(f"\n\n{error}\n\n") + + async def on_unhandled(self, unhandled, **kwargs): + print(f"\n\n{unhandled}\n\n") + + dg_connection.on(AgentWebSocketEvents.Open, on_open) + dg_connection.on(AgentWebSocketEvents.AudioData, on_binary_data) + dg_connection.on(AgentWebSocketEvents.Welcome, on_welcome) + dg_connection.on(AgentWebSocketEvents.SettingsApplied, on_settings_applied) + dg_connection.on(AgentWebSocketEvents.ConversationText, on_conversation_text) + dg_connection.on( + AgentWebSocketEvents.UserStartedSpeaking, on_user_started_speaking + ) + dg_connection.on(AgentWebSocketEvents.AgentThinking, on_agent_thinking) + dg_connection.on(AgentWebSocketEvents.FunctionCalling, on_function_calling) + dg_connection.on( + AgentWebSocketEvents.AgentStartedSpeaking, on_agent_started_speaking + ) + dg_connection.on(AgentWebSocketEvents.AgentAudioDone, on_agent_audio_done) + dg_connection.on(AgentWebSocketEvents.Close, on_close) + dg_connection.on(AgentWebSocketEvents.Error, on_error) + dg_connection.on(AgentWebSocketEvents.Unhandled, on_unhandled) + + # connect to websocket + options = SettingsConfigurationOptions() + options.agent.think.provider.type = "open_ai" + options.agent.think.model = "gpt-4o-mini" + options.agent.think.instructions = "You are a helpful AI assistant." + + print("\n\nPress Enter to stop...\n\n") + if await dg_connection.start(options) is False: + print("Failed to start connection") + return + + # wait until cancelled + try: + while True: + await asyncio.sleep(1) + except asyncio.CancelledError: + # This block will be executed when the shutdown coroutine cancels all tasks + pass + finally: + await dg_connection.finish() + + print("Finished") + + except ValueError as e: + print(f"Invalid value encountered: {e}") + except Exception as e: + print(f"An unexpected error occurred: {e}") + + +async def shutdown(signal, loop, dg_connection): + print(f"Received exit signal {signal.name}...") + await dg_connection.finish() + tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()] + [task.cancel() for task in tasks] + print(f"Cancelling {len(tasks)} outstanding tasks") + await asyncio.gather(*tasks, return_exceptions=True) + loop.stop() + print("Shutdown complete.") + + +asyncio.run(main()) diff --git a/examples/agent/simple/main.py b/examples/agent/simple/main.py new file mode 100644 index 00000000..d8ffef3a --- /dev/null +++ b/examples/agent/simple/main.py @@ -0,0 +1,127 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +from deepgram.utils import verboselogs + +from deepgram import ( + DeepgramClient, + DeepgramClientOptions, + AgentWebSocketEvents, + SettingsConfigurationOptions, +) + +TTS_TEXT = "Hello, this is a text to speech example using Deepgram." + +global warning_notice +warning_notice = True + + +def main(): + try: + # example of setting up a client config. logging values: WARNING, VERBOSE, DEBUG, SPAM + config: DeepgramClientOptions = DeepgramClientOptions( + options={ + "keepalive": "true", + "microphone_record": "true", + "speaker_playback": "true", + }, + # verbose=verboselogs.DEBUG, + ) + deepgram: DeepgramClient = DeepgramClient("", config) + + # Create a websocket connection to Deepgram + dg_connection = deepgram.agent.websocket.v("1") + + def on_open(self, open, **kwargs): + print(f"\n\n{open}\n\n") + + def on_binary_data(self, data, **kwargs): + global warning_notice + if warning_notice: + print("Received binary data") + print("You can do something with the binary data here") + print("OR") + print( + "If you want to simply play the audio, set speaker_playback to true in the options for DeepgramClientOptions" + ) + warning_notice = False + + def on_welcome(self, welcome, **kwargs): + print(f"\n\n{welcome}\n\n") + + def on_settings_applied(self, settings_applied, **kwargs): + print(f"\n\n{settings_applied}\n\n") + + def on_conversation_text(self, conversation_text, **kwargs): + print(f"\n\n{conversation_text}\n\n") + + def on_user_started_speaking(self, user_started_speaking, **kwargs): + print(f"\n\n{user_started_speaking}\n\n") + + def on_agent_thinking(self, agent_thinking, **kwargs): + print(f"\n\n{agent_thinking}\n\n") + + def on_function_calling(self, function_calling, **kwargs): + print(f"\n\n{function_calling}\n\n") + + def on_agent_started_speaking(self, agent_started_speaking, **kwargs): + print(f"\n\n{agent_started_speaking}\n\n") + + def on_agent_audio_done(self, agent_audio_done, **kwargs): + print(f"\n\n{agent_audio_done}\n\n") + + def on_close(self, close, **kwargs): + print(f"\n\n{close}\n\n") + + def on_error(self, error, **kwargs): + print(f"\n\n{error}\n\n") + + def on_unhandled(self, unhandled, **kwargs): + print(f"\n\n{unhandled}\n\n") + + dg_connection.on(AgentWebSocketEvents.Open, on_open) + dg_connection.on(AgentWebSocketEvents.AudioData, on_binary_data) + dg_connection.on(AgentWebSocketEvents.Welcome, on_welcome) + dg_connection.on(AgentWebSocketEvents.SettingsApplied, on_settings_applied) + dg_connection.on(AgentWebSocketEvents.ConversationText, on_conversation_text) + dg_connection.on( + AgentWebSocketEvents.UserStartedSpeaking, on_user_started_speaking + ) + dg_connection.on(AgentWebSocketEvents.AgentThinking, on_agent_thinking) + dg_connection.on(AgentWebSocketEvents.FunctionCalling, on_function_calling) + dg_connection.on( + AgentWebSocketEvents.AgentStartedSpeaking, on_agent_started_speaking + ) + dg_connection.on(AgentWebSocketEvents.AgentAudioDone, on_agent_audio_done) + dg_connection.on(AgentWebSocketEvents.Close, on_close) + dg_connection.on(AgentWebSocketEvents.Error, on_error) + dg_connection.on(AgentWebSocketEvents.Unhandled, on_unhandled) + + # connect to websocket + options: SettingsConfigurationOptions = SettingsConfigurationOptions() + options.agent.think.provider.type = "open_ai" + options.agent.think.model = "gpt-4o-mini" + options.agent.think.instructions = "You are a helpful AI assistant." + + print("\n\nPress Enter to stop...\n\n") + if dg_connection.start(options) is False: + print("Failed to start connection") + return + + print("\n\nPress Enter to stop...\n\n") + input() + + # Close the connection + dg_connection.finish() + + print("Finished") + + except ValueError as e: + print(f"Invalid value encountered: {e}") + except Exception as e: + print(f"An unexpected error occurred: {e}") + + +if __name__ == "__main__": + main() diff --git a/examples/speech-to-text/websocket/replay/main.py b/examples/speech-to-text/websocket/replay/main.py new file mode 100644 index 00000000..f83416fe --- /dev/null +++ b/examples/speech-to-text/websocket/replay/main.py @@ -0,0 +1,100 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +import httpx +from dotenv import load_dotenv +import logging +from deepgram.utils import verboselogs +import threading + +from deepgram import ( + DeepgramClient, + DeepgramClientOptions, + LiveTranscriptionEvents, + LiveOptions, +) + +load_dotenv() + + +def main(): + try: + # example of setting up a client config. logging values: WARNING, VERBOSE, DEBUG, SPAM + # config: DeepgramClientOptions = DeepgramClientOptions(verbose=verboselogs.DEBUG) + # deepgram: DeepgramClient = DeepgramClient("", config) + # otherwise, use default config + deepgram: DeepgramClient = DeepgramClient() + + # Create a websocket connection to Deepgram + dg_connection = deepgram.listen.websocket.v("1") + + def on_open(self, open, **kwargs): + print(f"\n\n{open}\n\n") + + def on_message(self, result, **kwargs): + sentence = result.channel.alternatives[0].transcript + if len(sentence) == 0: + return + print(f"speaker: {sentence}") + + # def on_metadata(self, metadata, **kwargs): + # print(f"\n\n{metadata}\n\n") + + # def on_speech_started(self, speech_started, **kwargs): + # print(f"\n\n{speech_started}\n\n") + + # def on_utterance_end(self, utterance_end, **kwargs): + # print(f"\n\n{utterance_end}\n\n") + + def on_close(self, close, **kwargs): + print(f"\n\n{close}\n\n") + + def on_error(self, error, **kwargs): + print(f"\n\n{error}\n\n") + + # def on_unhandled(self, unhandled, **kwargs): + # print(f"\n\n{unhandled}\n\n") + + dg_connection.on(LiveTranscriptionEvents.Open, on_open) + dg_connection.on(LiveTranscriptionEvents.Transcript, on_message) + # dg_connection.on(LiveTranscriptionEvents.Metadata, on_metadata) + # dg_connection.on(LiveTranscriptionEvents.SpeechStarted, on_speech_started) + # dg_connection.on(LiveTranscriptionEvents.UtteranceEnd, on_utterance_end) + dg_connection.on(LiveTranscriptionEvents.Close, on_close) + dg_connection.on(LiveTranscriptionEvents.Error, on_error) + # dg_connection.on(LiveTranscriptionEvents.Unhandled, on_unhandled) + + # connect to websocket + options = LiveOptions( + model="nova-2", + language="en-US", + encoding="linear16", + sample_rate=22050, + smart_format=True, + ) + + print("\n\nPress Enter to stop recording...\n\n") + if dg_connection.start(options) is False: + print("Failed to start connection") + return + + # open file for reading + with open("microsoft_headquarters.wav", "rb") as f: + dg_connection.send(f.read()) + + # signal finished + input("") + + # Indicate that we've finished + dg_connection.finish() + + print("Finished") + + except Exception as e: + print(f"Could not open socket: {e}") + return + + +if __name__ == "__main__": + main() diff --git a/examples/speech-to-text/websocket/replay/microsoft_headquarters.wav b/examples/speech-to-text/websocket/replay/microsoft_headquarters.wav new file mode 100644 index 0000000000000000000000000000000000000000..83fdfc180c9ebf6902b9001ac9108a4b189ae3ae GIT binary patch literal 224434 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