diff --git a/engine/core/duplex_pipeline.py b/engine/core/duplex_pipeline.py index 06052b0..44d3cad 100644 --- a/engine/core/duplex_pipeline.py +++ b/engine/core/duplex_pipeline.py @@ -73,6 +73,7 @@ class DuplexPipeline: _ASR_DELTA_THROTTLE_MS = 500 _LLM_DELTA_THROTTLE_MS = 80 _ASR_CAPTURE_MAX_MS = 15000 + _OPENER_PRE_ROLL_MS = 180 _DEFAULT_TOOL_SCHEMAS: Dict[str, Dict[str, Any]] = { "current_time": { "name": "current_time", @@ -781,6 +782,9 @@ class DuplexPipeline: ) await self.conversation.add_assistant_turn(greeting_to_speak) + # Give client mic capture a short head start so opener can be interrupted immediately. + await asyncio.sleep(self._OPENER_PRE_ROLL_MS / 1000.0) + used_preloaded_audio = await self._play_preloaded_opener_audio() if self._tts_output_enabled() and not used_preloaded_audio: # Keep opener text ahead of opener voice start. diff --git a/web/pages/Assistants.tsx b/web/pages/Assistants.tsx index e8af420..cb79bfd 100644 --- a/web/pages/Assistants.tsx +++ b/web/pages/Assistants.tsx @@ -178,7 +178,7 @@ export const AssistantsPage: React.FC = () => { hotwords: [], tools: [], botCannotBeInterrupted: false, - interruptionSensitivity: 500, + interruptionSensitivity: 180, configMode: 'platform', }; try { @@ -1049,7 +1049,7 @@ export const AssistantsPage: React.FC = () => {
updateAssistant('interruptionSensitivity', parseInt(e.target.value) || 0)} className="w-20 h-8 text-right pr-7 text-xs font-mono bg-black/40 border-white/5" /> @@ -1063,7 +1063,7 @@ export const AssistantsPage: React.FC = () => { min="0" max="2000" step="50" - value={selectedAssistant.interruptionSensitivity || 500} + value={selectedAssistant.interruptionSensitivity || 180} onChange={(e) => updateAssistant('interruptionSensitivity', parseInt(e.target.value))} className="flex-1 h-1.5 bg-secondary rounded-lg appearance-none cursor-pointer accent-primary" /> @@ -2075,13 +2075,14 @@ export const DebugDrawer: React.FC<{ setWsError(''); setDynamicVariablesError(''); closeWs(); - if (textTtsEnabled) await ensureAudioContext(); - await ensureWsSession(); - await startVoiceCapture(); - setCallStatus('active'); - } catch (e) { - console.error(e); - stopVoiceCapture(); + if (textTtsEnabled) await ensureAudioContext(); + // Start mic capture before session.start so barge-in works from opener start. + await startVoiceCapture(); + await ensureWsSession(); + setCallStatus('active'); + } catch (e) { + console.error(e); + stopVoiceCapture(); setCallStatus('idle'); const err = e as Error & { __dynamicVariables?: boolean }; if (err.__dynamicVariables) { @@ -2378,7 +2379,7 @@ export const DebugDrawer: React.FC<{ generatedOpenerEnabled: assistant.generatedOpenerEnabled === true, bargeIn: { enabled: assistant.botCannotBeInterrupted !== true, - minDurationMs: assistant.interruptionSensitivity || 500, + minDurationMs: Math.max(0, Number(assistant.interruptionSensitivity ?? 180)), }, knowledgeBaseId, knowledge,