Skip to content

Commit 3782edb

Browse files
Agent API Early Access
1 parent 84fc18b commit 3782edb

File tree

36 files changed

+3085
-313
lines changed

36 files changed

+3085
-313
lines changed

deepgram/__init__.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from .errors import DeepgramApiKeyError
3535

3636
# listen/read client
37-
from .client import Listen, Read
37+
from .client import ListenRouter, ReadRouter, SpeakRouter, AgentRouter
3838

3939
# common
4040
from .client import (
@@ -302,6 +302,56 @@
302302
AsyncSelfHostedClient,
303303
)
304304

305+
306+
# agent
307+
from .client import AgentWebSocketEvents
308+
309+
# websocket
310+
from .client import (
311+
AgentWebSocketClient,
312+
AsyncAgentWebSocketClient,
313+
)
314+
315+
from .client import (
316+
#### common websocket response
317+
# OpenResponse,
318+
# CloseResponse,
319+
# ErrorResponse,
320+
# UnhandledResponse,
321+
#### unique
322+
WelcomeResponse,
323+
SettingsAppliedResponse,
324+
ConversationTextResponse,
325+
UserStartedSpeakingResponse,
326+
AgentThinkingResponse,
327+
FunctionCallingResponse,
328+
AgentStartedSpeakingResponse,
329+
AgentAudioDoneResponse,
330+
)
331+
332+
from .client import (
333+
# top level
334+
SettingsConfigurationOptions,
335+
UpdateInstructionsOptions,
336+
UpdateSpeakOptions,
337+
InjectAgentMessageOptions,
338+
# sub level
339+
Listen,
340+
Speak,
341+
Header,
342+
Item,
343+
Properties,
344+
Parameters,
345+
Function,
346+
Provider,
347+
Think,
348+
Agent,
349+
Input,
350+
Output,
351+
Audio,
352+
Context,
353+
)
354+
305355
# utilities
306356
# pylint: disable=wrong-import-position
307357
from .audio import Microphone, DeepgramMicrophoneError

deepgram/audio/microphone/microphone.py

Lines changed: 83 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import logging
1010

1111
from ...utils import verboselogs
12+
1213
from .constants import LOGGING, CHANNELS, RATE, CHUNK
1314

1415
if TYPE_CHECKING:
@@ -22,8 +23,8 @@ class Microphone: # pylint: disable=too-many-instance-attributes
2223

2324
_logger: verboselogs.VerboseLogger
2425

25-
_audio: "pyaudio.PyAudio"
26-
_stream: "pyaudio.Stream"
26+
_audio: Optional["pyaudio.PyAudio"] = None
27+
_stream: Optional["pyaudio.Stream"] = None
2728

2829
_chunk: int
2930
_rate: int
@@ -145,77 +146,49 @@ def start(self) -> bool:
145146
self._asyncio_thread = None
146147
self._push_callback = self._push_callback_org
147148

148-
self._stream = self._audio.open(
149-
format=self._format,
150-
channels=self._channels,
151-
rate=self._rate,
152-
input=True,
153-
frames_per_buffer=self._chunk,
154-
input_device_index=self._input_device_index,
155-
stream_callback=self._callback,
156-
)
149+
if self._audio is not None:
150+
self._stream = self._audio.open(
151+
format=self._format,
152+
channels=self._channels,
153+
rate=self._rate,
154+
input=True,
155+
output=False,
156+
frames_per_buffer=self._chunk,
157+
input_device_index=self._input_device_index,
158+
stream_callback=self._callback,
159+
)
160+
161+
if self._stream is None:
162+
self._logger.error("start failed. No stream created.")
163+
self._logger.debug("Microphone.start LEAVE")
164+
return False
157165

158166
self._exit.clear()
159-
self._stream.start_stream()
167+
if self._stream is not None:
168+
self._stream.start_stream()
160169

161170
self._logger.notice("start succeeded")
162171
self._logger.debug("Microphone.start LEAVE")
163172
return True
164173

165-
def _callback(
166-
self, input_data, frame_count, time_info, status_flags
167-
): # pylint: disable=unused-argument
168-
"""
169-
The callback used to process data in callback mode.
170-
"""
171-
# dynamic import of pyaudio as not to force the requirements on the SDK (and users)
172-
import pyaudio # pylint: disable=import-outside-toplevel
173-
174-
self._logger.debug("Microphone._callback ENTER")
175-
176-
if self._exit.is_set():
177-
self._logger.info("exit is Set")
178-
self._logger.notice("_callback stopping...")
179-
self._logger.debug("Microphone._callback LEAVE")
180-
return None, pyaudio.paAbort
181-
182-
if input_data is None:
183-
self._logger.warning("input_data is None")
184-
self._logger.debug("Microphone._callback LEAVE")
185-
return None, pyaudio.paContinue
186-
187-
try:
188-
if self._is_muted:
189-
size = len(input_data)
190-
input_data = b"\x00" * size
191-
192-
self._push_callback(input_data)
193-
except Exception as e:
194-
self._logger.error("Error while sending: %s", str(e))
195-
self._logger.debug("Microphone._callback LEAVE")
196-
raise
197-
198-
self._logger.debug("Microphone._callback LEAVE")
199-
return input_data, pyaudio.paContinue
200-
201174
def mute(self) -> bool:
202175
"""
203176
mute - mutes the microphone stream
204177
205178
Returns:
206179
bool: True if the stream was muted, False otherwise
207180
"""
208-
self._logger.debug("Microphone.mute ENTER")
181+
self._logger.verbose("Microphone.mute ENTER")
209182

210183
if self._stream is None:
211184
self._logger.error("mute failed. Library not initialized.")
212-
self._logger.debug("Microphone.mute LEAVE")
185+
self._logger.verbose("Microphone.mute LEAVE")
213186
return False
214187

215188
self._is_muted = True
216189

217190
self._logger.notice("mute succeeded")
218-
self._logger.debug("Microphone.mute LEAVE")
191+
self._logger.verbose("Microphone.mute LEAVE")
219192
return True
220193

221194
def unmute(self) -> bool:
@@ -225,19 +198,42 @@ def unmute(self) -> bool:
225198
Returns:
226199
bool: True if the stream was unmuted, False otherwise
227200
"""
228-
self._logger.debug("Microphone.unmute ENTER")
201+
self._logger.verbose("Microphone.unmute ENTER")
229202

230203
if self._stream is None:
231204
self._logger.error("unmute failed. Library not initialized.")
232-
self._logger.debug("Microphone.unmute LEAVE")
205+
self._logger.verbose("Microphone.unmute LEAVE")
233206
return False
234207

235208
self._is_muted = False
236209

237210
self._logger.notice("unmute succeeded")
238-
self._logger.debug("Microphone.unmute LEAVE")
211+
self._logger.verbose("Microphone.unmute LEAVE")
239212
return True
240213

214+
def is_muted(self) -> bool:
215+
"""
216+
is_muted - returns the state of the stream
217+
218+
Args:
219+
None
220+
221+
Returns:
222+
True if the stream is muted, False otherwise
223+
"""
224+
self._logger.spam("Microphone.is_muted ENTER")
225+
226+
if self._stream is None:
227+
self._logger.spam("is_muted: stream is None")
228+
self._logger.spam("Microphone.is_muted LEAVE")
229+
return False
230+
231+
val = self._is_muted
232+
233+
self._logger.spam("is_muted: %s", val)
234+
self._logger.spam("Microphone.is_muted LEAVE")
235+
return val
236+
241237
def finish(self) -> bool:
242238
"""
243239
finish - stops the microphone stream
@@ -255,7 +251,6 @@ def finish(self) -> bool:
255251
self._logger.notice("stopping stream...")
256252
self._stream.stop_stream()
257253
self._stream.close()
258-
self._stream = None # type: ignore
259254
self._logger.notice("stream stopped")
260255

261256
# clean up the thread
@@ -265,13 +260,44 @@ def finish(self) -> bool:
265260
self._asyncio_thread
266261
is not None
267262
):
268-
self._logger.notice("stopping asyncio loop...")
263+
self._logger.notice("stopping _asyncio_loop...")
269264
self._asyncio_loop.call_soon_threadsafe(self._asyncio_loop.stop)
270265
self._asyncio_thread.join()
271-
self._asyncio_thread = None
272266
self._logger.notice("_asyncio_thread joined")
273267

268+
self._stream = None
269+
self._asyncio_thread = None
270+
274271
self._logger.notice("finish succeeded")
275272
self._logger.debug("Microphone.finish LEAVE")
276273

277274
return True
275+
276+
def _callback(
277+
self, input_data, frame_count, time_info, status_flags
278+
): # pylint: disable=unused-argument
279+
"""
280+
The callback used to process data in callback mode.
281+
"""
282+
# dynamic import of pyaudio as not to force the requirements on the SDK (and users)
283+
import pyaudio # pylint: disable=import-outside-toplevel
284+
285+
if self._exit.is_set():
286+
self._logger.notice("_callback exit is Set. stopping...")
287+
return None, pyaudio.paAbort
288+
289+
if input_data is None:
290+
self._logger.warning("input_data is None")
291+
return None, pyaudio.paContinue
292+
293+
try:
294+
if self._is_muted:
295+
size = len(input_data)
296+
input_data = b"\x00" * size
297+
298+
self._push_callback(input_data)
299+
except Exception as e:
300+
self._logger.error("Error while sending: %s", str(e))
301+
raise
302+
303+
return input_data, pyaudio.paContinue

0 commit comments

Comments
 (0)