fix long run bug
This commit is contained in:
@@ -113,6 +113,10 @@ class DuplexPipeline:
|
||||
# Interruption handling
|
||||
self._interrupt_event = asyncio.Event()
|
||||
|
||||
# Latency tracking - TTFB (Time to First Byte)
|
||||
self._turn_start_time: Optional[float] = None
|
||||
self._first_audio_sent: bool = False
|
||||
|
||||
# Barge-in filtering - require minimum speech duration to interrupt
|
||||
self._barge_in_speech_start_time: Optional[float] = None
|
||||
self._barge_in_min_duration_ms: int = settings.barge_in_min_duration_ms if hasattr(settings, 'barge_in_min_duration_ms') else 50
|
||||
@@ -396,6 +400,10 @@ class DuplexPipeline:
|
||||
user_text: User's transcribed text
|
||||
"""
|
||||
try:
|
||||
# Start latency tracking
|
||||
self._turn_start_time = time.time()
|
||||
self._first_audio_sent = False
|
||||
|
||||
# Get AI response (streaming)
|
||||
messages = self.conversation.get_messages()
|
||||
full_response = ""
|
||||
@@ -495,10 +503,33 @@ class DuplexPipeline:
|
||||
|
||||
try:
|
||||
async for chunk in self.tts_service.synthesize_stream(text):
|
||||
# Check interrupt at the start of each iteration
|
||||
if self._interrupt_event.is_set():
|
||||
logger.debug("TTS sentence interrupted")
|
||||
break
|
||||
|
||||
# Track and log first audio packet latency (TTFB)
|
||||
if not self._first_audio_sent and self._turn_start_time:
|
||||
ttfb_ms = (time.time() - self._turn_start_time) * 1000
|
||||
self._first_audio_sent = True
|
||||
logger.info(f"[TTFB] Server first audio packet latency: {ttfb_ms:.0f}ms (session {self.session_id})")
|
||||
|
||||
# Send TTFB event to client
|
||||
await self.transport.send_event({
|
||||
"event": "ttfb",
|
||||
"trackId": self.session_id,
|
||||
"timestamp": self._get_timestamp_ms(),
|
||||
"latencyMs": round(ttfb_ms)
|
||||
})
|
||||
|
||||
# Double-check interrupt right before sending audio
|
||||
if self._interrupt_event.is_set():
|
||||
break
|
||||
|
||||
await self.transport.send_audio(chunk.audio)
|
||||
await asyncio.sleep(0.005) # Small delay to prevent flooding
|
||||
except asyncio.CancelledError:
|
||||
logger.debug("TTS sentence cancelled")
|
||||
except Exception as e:
|
||||
logger.error(f"TTS sentence error: {e}")
|
||||
|
||||
@@ -513,6 +544,10 @@ class DuplexPipeline:
|
||||
return
|
||||
|
||||
try:
|
||||
# Start latency tracking for greeting
|
||||
speak_start_time = time.time()
|
||||
first_audio_sent = False
|
||||
|
||||
# Send track start event
|
||||
await self.transport.send_event({
|
||||
"event": "trackStart",
|
||||
@@ -528,6 +563,20 @@ class DuplexPipeline:
|
||||
logger.info("TTS interrupted by barge-in")
|
||||
break
|
||||
|
||||
# Track and log first audio packet latency (TTFB)
|
||||
if not first_audio_sent:
|
||||
ttfb_ms = (time.time() - speak_start_time) * 1000
|
||||
first_audio_sent = True
|
||||
logger.info(f"[TTFB] Greeting first audio packet latency: {ttfb_ms:.0f}ms (session {self.session_id})")
|
||||
|
||||
# Send TTFB event to client
|
||||
await self.transport.send_event({
|
||||
"event": "ttfb",
|
||||
"trackId": self.session_id,
|
||||
"timestamp": self._get_timestamp_ms(),
|
||||
"latencyMs": round(ttfb_ms)
|
||||
})
|
||||
|
||||
# Send audio to client
|
||||
await self.transport.send_audio(chunk.audio)
|
||||
|
||||
@@ -561,8 +610,17 @@ class DuplexPipeline:
|
||||
self._barge_in_speech_frames = 0
|
||||
self._barge_in_silence_frames = 0
|
||||
|
||||
# Signal interruption
|
||||
# IMPORTANT: Signal interruption FIRST to stop audio sending
|
||||
self._interrupt_event.set()
|
||||
self._is_bot_speaking = False
|
||||
|
||||
# Send interrupt event to client IMMEDIATELY
|
||||
# This must happen BEFORE canceling services, so client knows to discard in-flight audio
|
||||
await self.transport.send_event({
|
||||
"event": "interrupt",
|
||||
"trackId": self.session_id,
|
||||
"timestamp": self._get_timestamp_ms()
|
||||
})
|
||||
|
||||
# Cancel TTS
|
||||
if self.tts_service:
|
||||
@@ -575,15 +633,7 @@ class DuplexPipeline:
|
||||
# Interrupt conversation
|
||||
await self.conversation.interrupt()
|
||||
|
||||
# Send interrupt event to client
|
||||
await self.transport.send_event({
|
||||
"event": "interrupt",
|
||||
"trackId": self.session_id,
|
||||
"timestamp": self._get_timestamp_ms()
|
||||
})
|
||||
|
||||
# Reset for new user turn
|
||||
self._is_bot_speaking = False
|
||||
await self.conversation.start_user_turn()
|
||||
self._audio_buffer = b""
|
||||
self.eou_detector.reset()
|
||||
|
||||
Reference in New Issue
Block a user