Skip to content

Commit 0fb8324

Browse files
committed
Checking in
1 parent e387bc4 commit 0fb8324

File tree

4 files changed

+35
-96
lines changed

4 files changed

+35
-96
lines changed

README.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,36 @@ Then, create a new instance of the Vapi class, passing your API Key as a paramet
2424
vapi = Vapi(api_key='your-api-key')
2525
```
2626

27-
You can start a new call by calling the `start` method and passing an `assistant` object or `assistantId`:
27+
You can start a new call by calling the `start` method and passing an `assistant` object or `assistantId`. You can find the available options here: [docs.vapi.ai](https://docs.vapi.ai/api-reference/assistants/create-assistant)
2828

2929
```python
3030
vapi.start(assistant_id='your-assistant-id')
3131
```
3232
or
3333
```python
34-
vapi.start(assistant={'context': 'You are a shopping assistant...', 'voice': 'steve', ...})
34+
assistant = {
35+
'firstMessage': 'Hey, how are you?',
36+
'context': 'You are a shopping assistant...',
37+
'model': 'gpt-3.5-turbo',
38+
'voice': 'jennifer-playht',
39+
"recordingEnabled": True,
40+
"functions": [
41+
{
42+
"name": "setColor",
43+
"description": "Used to set the color",
44+
"parameters": {
45+
"type": "object",
46+
"properties": {
47+
"color": {
48+
"type": "string"
49+
}
50+
}
51+
}
52+
}
53+
]
54+
}
55+
56+
vapi.start(assistant=assistant)
3557
```
3658

3759
The `start` method will initiate a new call.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,6 @@ def read_requirements(file):
4949
test_suite='tests',
5050
tests_require=test_requirements,
5151
url='https://github.com/jordan.cde/vapi_python',
52-
version='0.1.3',
52+
version='0.1.4',
5353
zip_safe=False,
5454
)

vapi_python/daily_call.py

Lines changed: 8 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import daily
22
import threading
33
import pyaudio
4-
import wave
5-
from speexdsp import EchoCanceller
64

75
SAMPLE_RATE = 16000
86
NUM_CHANNELS = 1
@@ -17,75 +15,11 @@ def is_playable_speaker(participant):
1715
return is_speaker and is_subscribed and is_playable
1816

1917

20-
class EchoCancellerWrapper():
21-
def __init__(self, chunk_size, filter_length, sample_rate, audio_interface):
22-
self.__audio_interface = audio_interface
23-
self.__echo_canceller = EchoCanceller.create(
24-
int(chunk_size / 2), filter_length, sample_rate)
25-
self.__mic_buffer = b''
26-
self.__speaker_buffer = b''
27-
28-
# Initialize wav files for mic input, speaker input, and processed output
29-
self.__mic_wav = wave.open('mic_input.wav', 'wb')
30-
self.__mic_wav.setnchannels(NUM_CHANNELS)
31-
self.__mic_wav.setsampwidth(
32-
self.__audio_interface.get_sample_size(pyaudio.paInt16))
33-
self.__mic_wav.setframerate(SAMPLE_RATE)
34-
35-
self.__speaker_wav = wave.open('speaker_input.wav', 'wb')
36-
self.__speaker_wav.setnchannels(NUM_CHANNELS)
37-
self.__speaker_wav.setsampwidth(
38-
self.__audio_interface.get_sample_size(pyaudio.paInt16))
39-
self.__speaker_wav.setframerate(SAMPLE_RATE)
40-
41-
self.__output_wav = wave.open('processed_output.wav', 'wb')
42-
self.__output_wav.setnchannels(NUM_CHANNELS)
43-
self.__output_wav.setsampwidth(
44-
self.__audio_interface.get_sample_size(pyaudio.paInt16))
45-
self.__output_wav.setframerate(SAMPLE_RATE)
46-
47-
def send_mic_audio(self, audio):
48-
self.__mic_buffer += audio
49-
50-
def send_speaker_audio(self, audio):
51-
self.__speaker_buffer += audio
52-
53-
def read(self, length):
54-
min_length = min(len(self.__mic_buffer), length)
55-
if min_length > 0:
56-
mic_audio = self.__mic_buffer[:min_length]
57-
speaker_audio = self.__speaker_buffer[:min_length]
58-
# If speaker buffer is less than mic buffer, pad it with silence
59-
if len(speaker_audio) < len(mic_audio):
60-
speaker_audio += b'\x00' * \
61-
(len(mic_audio) - len(speaker_audio))
62-
63-
self.__mic_wav.writeframes(mic_audio)
64-
self.__speaker_wav.writeframes(speaker_audio)
65-
66-
self.__mic_buffer = self.__mic_buffer[min_length:]
67-
self.__speaker_buffer = self.__speaker_buffer[min_length:]
68-
processed_audio = self.__echo_canceller.process(
69-
mic_audio, speaker_audio)
70-
# Write processed output to wav file
71-
self.__output_wav.writeframes(processed_audio)
72-
return processed_audio
73-
return b''
74-
75-
def __del__(self):
76-
# Close wav files when the object is destroyed
77-
self.__mic_wav.close()
78-
self.__speaker_wav.close()
79-
self.__output_wav.close()
80-
81-
8218
class DailyCall(daily.EventHandler):
8319
def __init__(self):
8420
daily.Daily.init()
8521

8622
self.__audio_interface = pyaudio.PyAudio()
87-
self.__echo_canceller = EchoCancellerWrapper(
88-
CHUNK_SIZE, 256, SAMPLE_RATE, self.__audio_interface)
8923

9024
self.__input_audio_stream = self.__audio_interface.open(
9125
format=pyaudio.paInt16,
@@ -146,14 +80,11 @@ def __init__(self):
14680
self.__start_event = threading.Event()
14781
self.__receive_bot_audio_thread = threading.Thread(
14882
target=self.receive_bot_audio)
149-
self.__receive_mic_audio_thread = threading.Thread(
150-
target=self.receive_mic_audio)
151-
self.__send_echo_cancelled_mic_audio_thread = threading.Thread(
152-
target=self.send_echo_cancelled_mic_audio)
83+
self.__send_user_audio_thread = threading.Thread(
84+
target=self.send_user_audio)
15385

15486
self.__receive_bot_audio_thread.start()
155-
self.__receive_mic_audio_thread.start()
156-
self.__send_echo_cancelled_mic_audio_thread.start()
87+
self.__send_user_audio_thread.start()
15788

15889
def on_inputs_updated(self, inputs):
15990
self.__app_inputs_updated = True
@@ -186,8 +117,7 @@ def join(self, meeting_url):
186117
def leave(self):
187118
self.__app_quit = True
188119
self.__receive_bot_audio_thread.join()
189-
self.__receive_mic_audio_thread.join()
190-
self.__send_echo_cancelled_mic_audio_thread.join()
120+
self.__send_user_audio_thread.join()
191121
self.__call_client.leave()
192122

193123
def maybe_start(self):
@@ -197,33 +127,22 @@ def maybe_start(self):
197127
if self.__app_inputs_updated and self.__app_joined:
198128
self.__start_event.set()
199129

200-
def send_echo_cancelled_mic_audio(self):
130+
def send_user_audio(self):
201131
self.__start_event.wait()
202132

203133
if self.__app_error:
204-
print(f"Unable to send mic audio!")
134+
print(f"Unable to receive mic audio!")
205135
return
206136

207137
while not self.__app_quit:
208-
buffer = self.__echo_canceller.read(CHUNK_SIZE)
138+
buffer = self.__input_audio_stream.read(
139+
CHUNK_SIZE, exception_on_overflow=False)
209140
if len(buffer) > 0:
210141
try:
211142
self.__mic_device.write_frames(buffer)
212143
except Exception as e:
213144
print(e)
214145

215-
def receive_mic_audio(self):
216-
self.__start_event.wait()
217-
218-
if self.__app_error:
219-
print(f"Unable to receive mic audio!")
220-
return
221-
222-
while not self.__app_quit:
223-
buffer = self.__input_audio_stream.read(
224-
CHUNK_SIZE, exception_on_overflow=False)
225-
self.__echo_canceller.send_mic_audio(buffer)
226-
227146
def receive_bot_audio(self):
228147
self.__start_event.wait()
229148

@@ -233,7 +152,6 @@ def receive_bot_audio(self):
233152

234153
while not self.__app_quit:
235154
buffer = self.__speaker_device.read_frames(CHUNK_SIZE)
236-
self.__echo_canceller.send_speaker_audio(buffer)
237155

238156
if len(buffer) > 0:
239157
self.__output_audio_stream.write(buffer, CHUNK_SIZE)

vapi_python/vapi_python.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,13 @@ def create_web_call(api_url, api_key, assistant):
1313
'Content-Type': 'application/json'
1414
}
1515
response = requests.post(url, headers=headers, json=assistant)
16-
16+
data = response.json()
1717
if response.status_code == 201:
18-
data = response.json()
1918
call_id = data.get('id')
2019
web_call_url = data.get('webCallUrl')
2120
return call_id, web_call_url
2221
else:
23-
print(f"Error: {response.status_code}")
22+
print(f"Error: {data['message']}")
2423
return None
2524

2625

0 commit comments

Comments
 (0)