From 80593ae0498e2bda3dd4a2d60f30c9959e84b327 Mon Sep 17 00:00:00 2001 From: Yulin Li Date: Mon, 7 Sep 2026 01:13:50 +0800 Subject: [PATCH] Fix redundant Voice Live response cancellation on speech start Let the service interrupt active responses and keep local playback clearing in the Python, JavaScript, Java, and .NET agent quickstarts. Remove cancellation-only state and error suppression. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- dotnet/Speech/VoiceLiveWithAgentV2.cs | 34 +------------------ java/Speech/VoiceLiveWithAgentV2.java | 29 ++-------------- javascript/speech/voice-live-with-agent-v2.js | 25 -------------- python/Speech/voice-live-with-agent-v2.py | 24 ++----------- 4 files changed, 5 insertions(+), 107 deletions(-) diff --git a/dotnet/Speech/VoiceLiveWithAgentV2.cs b/dotnet/Speech/VoiceLiveWithAgentV2.cs index 7d1c4bb4..e32b2533 100644 --- a/dotnet/Speech/VoiceLiveWithAgentV2.cs +++ b/dotnet/Speech/VoiceLiveWithAgentV2.cs @@ -181,8 +181,6 @@ class BasicVoiceAssistant : IDisposable private VoiceLiveSession? _session; private AudioProcessor? _audioProcessor; private bool _greetingSent; - private bool _activeResponse; - private bool _responseApiDone; // Conversation log private static readonly string LogFilename = $"conversation_{DateTime.Now:yyyyMMdd_HHmmss}.log"; @@ -341,30 +339,12 @@ private async Task HandleEventAsync(SessionUpdate serverEvent, CancellationToken case SessionUpdateInputAudioBufferSpeechStarted: Console.WriteLine("🎤 Listening..."); _audioProcessor?.SkipPendingAudio(); - - // Cancel in-progress response for barge-in - if (_activeResponse && !_responseApiDone) - { - try - { - await _session!.CancelResponseAsync(cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) when (ex.Message?.Contains("no active response") == true) - { - // Benign - response already completed - } - } break; case SessionUpdateInputAudioBufferSpeechStopped: Console.WriteLine("🤔 Processing..."); break; - case SessionUpdateResponseCreated: - _activeResponse = true; - _responseApiDone = false; - break; - case SessionUpdateResponseAudioDelta audioDelta: if (audioDelta.Delta != null) { @@ -376,21 +356,9 @@ private async Task HandleEventAsync(SessionUpdate serverEvent, CancellationToken Console.WriteLine("🎤 Ready for next input..."); break; - case SessionUpdateResponseDone: - _activeResponse = false; - _responseApiDone = true; - break; - case SessionUpdateError errorEvent: var errorMsg = errorEvent.Error?.Message; - if (errorMsg?.Contains("Cancellation failed: no active response") == true) - { - // Benign cancellation error - } - else - { - Console.Error.WriteLine($"VoiceLive error: {errorMsg}"); - } + Console.Error.WriteLine($"VoiceLive error: {errorMsg}"); break; } } diff --git a/java/Speech/VoiceLiveWithAgentV2.java b/java/Speech/VoiceLiveWithAgentV2.java index 69551e08..25e7333e 100644 --- a/java/Speech/VoiceLiveWithAgentV2.java +++ b/java/Speech/VoiceLiveWithAgentV2.java @@ -6,7 +6,6 @@ import com.azure.ai.voicelive.VoiceLiveSessionAsyncClient; import com.azure.ai.voicelive.models.AgentSessionConfig; import com.azure.ai.voicelive.models.ClientEventConversationItemCreate; -import com.azure.ai.voicelive.models.ClientEventResponseCancel; import com.azure.ai.voicelive.models.ClientEventResponseCreate; import com.azure.ai.voicelive.models.ClientEventSessionUpdate; import com.azure.ai.voicelive.models.ConversationRequestItem; @@ -237,8 +236,6 @@ static class BasicVoiceAssistant { private AudioProcessor audioProcessor; private boolean sessionReady = false; private boolean greetingSent = false; - private boolean activeResponse = false; - private boolean responseApiDone = false; // BasicVoiceAssistant(String endpoint, String agentName, String projectName, @@ -393,28 +390,12 @@ private void handleEvent(SessionUpdate event) { System.out.println("🎤 Listening..."); audioProcessor.skipPendingAudio(); - // Cancel in-progress response for barge-in - if (activeResponse && !responseApiDone) { - try { - session.sendEvent(new ClientEventResponseCancel()).block(); - logger.fine("Cancelled in-progress response due to barge-in"); - } catch (Exception e) { - if (e.getMessage() != null && e.getMessage().toLowerCase().contains("no active response")) { - logger.fine("Cancel ignored - response already completed"); - } else { - logger.warning("Cancel failed: " + e.getMessage()); - } - } - } - } else if (type == ServerEventType.INPUT_AUDIO_BUFFER_SPEECH_STOPPED) { logger.info("User stopped speaking"); System.out.println("🤔 Processing..."); } else if (type == ServerEventType.RESPONSE_CREATED) { logger.info("Assistant response created"); - activeResponse = true; - responseApiDone = false; } else if (type == ServerEventType.RESPONSE_AUDIO_DELTA) { logger.fine("Received audio delta"); @@ -430,18 +411,12 @@ private void handleEvent(SessionUpdate event) { } else if (type == ServerEventType.RESPONSE_DONE) { logger.info("Response complete"); - activeResponse = false; - responseApiDone = true; } else if (type == ServerEventType.ERROR) { SessionUpdateError errorEvent = (SessionUpdateError) event; String msg = errorEvent.getError().getMessage(); - if (msg != null && msg.contains("Cancellation failed: no active response")) { - logger.fine("Benign cancellation error: " + msg); - } else { - logger.severe("VoiceLive error: " + msg); - System.out.println("Error: " + msg); - } + logger.severe("VoiceLive error: " + msg); + System.out.println("Error: " + msg); } else { logger.fine("Unhandled event type: " + type); diff --git a/javascript/speech/voice-live-with-agent-v2.js b/javascript/speech/voice-live-with-agent-v2.js index dbff6d8b..66515b0f 100644 --- a/javascript/speech/voice-live-with-agent-v2.js +++ b/javascript/speech/voice-live-with-agent-v2.js @@ -164,8 +164,6 @@ class BasicVoiceAssistant { this._session = null; this._audio = new AudioProcessor(); this._greetingSent = false; - this._activeResponse = false; - this._responseApiDone = false; } /** Connect, subscribe to events, and run until interrupted. */ @@ -253,29 +251,12 @@ class BasicVoiceAssistant { onInputAudioBufferSpeechStarted: async () => { console.log("🎤 Listening..."); this._audio.skipPendingAudio(); - - // Cancel in-progress response (barge-in) - if (this._activeResponse && !this._responseApiDone) { - try { - await session.sendEvent({ type: "response.cancel" }); - } catch (err) { - const msg = err?.message ?? ""; - if (!msg.toLowerCase().includes("no active response")) { - console.warn("[barge-in] Cancel failed:", msg); - } - } - } }, onInputAudioBufferSpeechStopped: async () => { console.log("🤔 Processing..."); }, - onResponseCreated: async () => { - this._activeResponse = true; - this._responseApiDone = false; - }, - onResponseAudioDelta: async (event) => { if (event.delta) { this._audio.queueAudio(event.delta); @@ -288,16 +269,10 @@ class BasicVoiceAssistant { onResponseDone: async () => { console.log("✅ Response complete"); - this._activeResponse = false; - this._responseApiDone = true; }, onServerError: async (event) => { const msg = event.error?.message ?? ""; - if (msg.includes("Cancellation failed: no active response")) { - // Benign – ignore - return; - } console.error(`❌ VoiceLive error: ${msg}`); }, diff --git a/python/Speech/voice-live-with-agent-v2.py b/python/Speech/voice-live-with-agent-v2.py index 5b4a7890..d233dd96 100644 --- a/python/Speech/voice-live-with-agent-v2.py +++ b/python/Speech/voice-live-with-agent-v2.py @@ -273,8 +273,6 @@ def __init__( self.audio_processor: Optional[AudioProcessor] = None self.session_ready = False self.greeting_sent = False - self._active_response = False - self._response_api_done = False async def start(self) -> None: """Start the voice assistant session.""" @@ -425,25 +423,12 @@ async def _handle_event(self, event: Any) -> None: ap.skip_pending_audio() - # Only cancel if response is active and not already done - if self._active_response and not self._response_api_done: - try: - await conn.response.cancel() - logger.debug("Cancelled in-progress response due to barge-in") - except Exception as e: - if "no active response" in str(e).lower(): - logger.debug("Cancel ignored - response already completed") - else: - logger.warning("Cancel failed: %s", e) - elif event.type == ServerEventType.INPUT_AUDIO_BUFFER_SPEECH_STOPPED: logger.info("🎤 User stopped speaking") print("🤔 Processing...") elif event.type == ServerEventType.RESPONSE_CREATED: logger.info("🤖 Assistant response created") - self._active_response = True - self._response_api_done = False elif event.type == ServerEventType.RESPONSE_AUDIO_DELTA: logger.debug("Received audio delta") @@ -455,16 +440,11 @@ async def _handle_event(self, event: Any) -> None: elif event.type == ServerEventType.RESPONSE_DONE: logger.info("✅ Response complete") - self._active_response = False - self._response_api_done = True elif event.type == ServerEventType.ERROR: msg = event.error.message - if "Cancellation failed: no active response" in msg: - logger.debug("Benign cancellation error: %s", msg) - else: - logger.error("❌ VoiceLive error: %s", msg) - print(f"Error: {msg}") + logger.error("❌ VoiceLive error: %s", msg) + print(f"Error: {msg}") elif event.type == ServerEventType.CONVERSATION_ITEM_CREATED: logger.debug("Conversation item created: %s", event.item.id)