Skip to content

Commit 03b08bd

Browse files
committed
Updated VAD settings and VAD behavior with mute/unmute in frontend
1 parent 24be94d commit 03b08bd

3 files changed

Lines changed: 63 additions & 11 deletions

File tree

python/voice-live-voicerag-assistant/app/backend/web_handler.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
ServerEventResponseFunctionCallArgumentsDone,
2828
AudioInputTranscriptionOptions,
2929
AzureSemanticVad,
30+
AzureSemanticDetection,
3031
MessageItem,
3132
ResponseCreateParams,
3233
)
@@ -237,6 +238,35 @@ async def _setup_session(self, connection):
237238
try:
238239
# Create session configuration
239240
try:
241+
# Determine if end_of_utterance_detection is supported
242+
# It's only supported for cascaded pipelines, not gpt-realtime models
243+
realtime_models = ["gpt-realtime", "gpt-realtime-mini", "phi4-mm-realtime"]
244+
supports_eou_detection = self.model not in realtime_models
245+
246+
# Build turn detection configuration
247+
if supports_eou_detection:
248+
turn_detection = AzureSemanticVad(
249+
threshold=0.3,
250+
prefix_padding_ms=300,
251+
speech_duration_ms=80,
252+
silence_duration_ms=500,
253+
remove_filler_words=True,
254+
interrupt_response=True,
255+
end_of_utterance_detection=AzureSemanticDetection(
256+
threshold_level="default",
257+
timeout_ms=1000
258+
)
259+
)
260+
else:
261+
turn_detection = AzureSemanticVad(
262+
threshold=0.3,
263+
prefix_padding_ms=300,
264+
speech_duration_ms=80,
265+
silence_duration_ms=500,
266+
remove_filler_words=True,
267+
interrupt_response=True,
268+
)
269+
240270
session_config = RequestSession(
241271
modalities=[Modality.TEXT, Modality.AUDIO],
242272
instructions=self.instructions,
@@ -246,11 +276,7 @@ async def _setup_session(self, connection):
246276
input_audio_transcription=AudioInputTranscriptionOptions(
247277
model=self.transcribe_model
248278
),
249-
turn_detection=AzureSemanticVad(
250-
threshold=0.5,
251-
prefix_padding_ms=300,
252-
silence_duration_ms=200,
253-
),
279+
turn_detection=turn_detection,
254280
tools=self.tools,
255281
tool_choice=ToolChoiceLiteral.AUTO,
256282
temperature=0.6,

python/voice-live-voicerag-assistant/app/frontend/src/App.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ function App() {
3737
startSession,
3838
stopSession,
3939
sendAudio,
40-
interruptAssistant
40+
interruptAssistant,
41+
setHonorVadInterruption
4142
} = useVoiceAssistant({
4243
onWebSocketOpen: () => {
4344
console.log("WebSocket connection opened to voice assistant");
@@ -64,7 +65,8 @@ function App() {
6465
},
6566
onSpeechStarted: () => {
6667
console.log("User started speaking");
67-
stopAudioPlayer();
68+
// Note: Removed automatic stopAudioPlayer() to allow pure mute/unmute
69+
// Use the Interrupt button to manually stop assistant playback
6870
},
6971
onSpeechStopped: () => {
7072
console.log("User stopped speaking");
@@ -174,9 +176,14 @@ function App() {
174176

175177
const onToggleListening = async () => {
176178
if (!isRecording && isSessionActive) {
179+
// Starting to listen (unmuting) - enable VAD-triggered barge-in
180+
setHonorVadInterruption(true);
177181
await startAudioRecording();
178182
setIsRecording(true);
179183
} else if (isRecording) {
184+
// Stopping listening (muting) - disable VAD-triggered barge-in first
185+
// This prevents the mute action from triggering a stop_playback
186+
setHonorVadInterruption(false);
180187
await stopAudioRecording();
181188
setIsRecording(false);
182189
}

python/voice-live-voicerag-assistant/app/frontend/src/hooks/useVoiceAssistant.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,12 @@ export default function useVoiceAssistant({
139139

140140
const clientIdRef = useRef(clientId || `client-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`);
141141

142-
const getWebSocketUrl = () => {
142+
// Track whether to honor VAD-triggered stop_playback messages
143+
// When muting (stopping listening), we ignore stop_playback because muting shouldn't interrupt playback
144+
// When unmuting (starting listening), we honor stop_playback so VAD can trigger barge-in
145+
const honorVadInterruptionRef = useRef(false);
146+
147+
const getWebSocketUrl = () => {
143148
if (serverUrl) return serverUrl;
144149

145150
// Auto-detect based on current page URL
@@ -315,9 +320,15 @@ export default function useVoiceAssistant({
315320
break;
316321

317322
case 'stop_playback':
318-
console.log('🛑 Stopping audio playback due to user interruption');
319-
audioPlayer.stop();
320-
onAudioPlaybackStop?.();
323+
// Only honor VAD-triggered stop_playback when actively listening (unmuted)
324+
// This prevents muting the mic from interrupting playback
325+
if (honorVadInterruptionRef.current) {
326+
console.log('🛑 Stopping audio playback due to user interruption (VAD barge-in)');
327+
audioPlayer.stop();
328+
onAudioPlaybackStop?.();
329+
} else {
330+
console.log('ℹ️ Ignoring stop_playback - microphone is muted');
331+
}
321332
break;
322333

323334
case 'user_speech_ended':
@@ -458,6 +469,13 @@ export default function useVoiceAssistant({
458469
const isConnecting = readyState === 0; // WebSocket.CONNECTING
459470
const isDisconnected = readyState === 3; // WebSocket.CLOSED
460471

472+
// Control whether VAD-triggered stop_playback should be honored
473+
// Call with true when starting to listen (unmuting), false when stopping (muting)
474+
const setHonorVadInterruption = useCallback((honor: boolean) => {
475+
honorVadInterruptionRef.current = honor;
476+
console.log(`VAD interruption ${honor ? 'enabled' : 'disabled'}`);
477+
}, []);
478+
461479
return {
462480
// Connection state
463481
isConnected,
@@ -471,6 +489,7 @@ export default function useVoiceAssistant({
471489
sendAudio,
472490
sendAudioChunk, // For real-time audio streaming
473491
interruptAssistant,
492+
setHonorVadInterruption, // Control VAD barge-in behavior
474493

475494
// Audio player controls
476495
audioPlayer, // Expose audio player for direct control

0 commit comments

Comments
 (0)