fix(workflow): prevent untracked BotStoppedSpeakingFrame from consuming end-node tracked-speech counter

The CallEndCoordinator.observe() decremented _tracked_speeches on every
BotStoppedSpeakingFrame, not just the one corresponding to tracked
(end-node) speech.  When a preceding LLM utterance finished between
track_speech() and the actual fixed-speech playback, the counter was
consumed prematurely, ending the call before the end-node message played.

Gate the decrement behind a _current_speech_is_tracked flag that is set
only when BotStartedSpeakingFrame follows a pending tracked-speech start.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ubuntu
2026-08-05 22:35:06 +08:00
parent 0c0a51da95
commit 8bd781242a

View File

@@ -27,6 +27,12 @@ class CallEndCoordinator:
self._finish_after_tracked_speech = False
self._finished = False
self._reason = "completed"
# Only BotStoppedSpeakingFrame that follows a tracked BotStartedSpeakingFrame
# decrements the tracked-speech counter. This prevents a stale or cross-talk
# stop frame (e.g. from a preceding LLM utterance) from consuming the counter
# meant for a fixed end-node message.
self._pending_tracked_starts = 0
self._current_speech_is_tracked = False
@property
def ending(self) -> bool:
@@ -58,6 +64,7 @@ class CallEndCoordinator:
completion = asyncio.get_running_loop().create_future()
self._tracked_speech_completions.append(completion)
self._tracked_speeches += 1
self._pending_tracked_starts += 1
return completion
async def arm_after_tracked_speech(self) -> None:
@@ -87,9 +94,14 @@ class CallEndCoordinator:
self._speaking = True
self._speech_stopped.clear()
self._response_speech_started = True
if self._pending_tracked_starts > 0:
self._pending_tracked_starts -= 1
self._current_speech_is_tracked = True
elif isinstance(frame, BotStoppedSpeakingFrame) and self._speaking:
self._speaking = False
self._speech_stopped.set()
if self._current_speech_is_tracked:
self._current_speech_is_tracked = False
if self._tracked_speeches > 0:
self._tracked_speeches -= 1
completion = self._tracked_speech_completions.popleft()