update timestamps reset handling
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
- Added support for Inworld TTS Websocket Auto Mode for improved latency
|
|
||||||
@@ -499,6 +499,14 @@ class InworldTTSService(AudioContextWordTTSService):
|
|||||||
self._context_id = None
|
self._context_id = None
|
||||||
self._started = False
|
self._started = False
|
||||||
|
|
||||||
|
# Track cumulative time across generations for monotonic timestamps within a turn.
|
||||||
|
# When auto_mode is enabled, the server controls generations and timestamps reset
|
||||||
|
# to 0 after each generation, as indicated by a "flushCompleted" message. We
|
||||||
|
# add _cumulative_time to maintain monotonically increasing timestamps.
|
||||||
|
self._cumulative_time = 0.0
|
||||||
|
# Track the end time of the last word in the current generation
|
||||||
|
self._generation_end_time = 0.0
|
||||||
|
|
||||||
self.set_voice(voice_id)
|
self.set_voice(voice_id)
|
||||||
self.set_model_name(model)
|
self.set_model_name(model)
|
||||||
|
|
||||||
@@ -559,27 +567,50 @@ class InworldTTSService(AudioContextWordTTSService):
|
|||||||
await super().push_frame(frame, direction)
|
await super().push_frame(frame, direction)
|
||||||
if isinstance(frame, (TTSStoppedFrame, InterruptionFrame)):
|
if isinstance(frame, (TTSStoppedFrame, InterruptionFrame)):
|
||||||
self._started = False
|
self._started = False
|
||||||
|
logger.trace(
|
||||||
|
f"{self}: Resetting timestamp tracking due to {type(frame).__name__} - "
|
||||||
|
f"cumulative_time was {self._cumulative_time}"
|
||||||
|
)
|
||||||
|
self._cumulative_time = 0.0
|
||||||
|
self._generation_end_time = 0.0
|
||||||
if isinstance(frame, TTSStoppedFrame):
|
if isinstance(frame, TTSStoppedFrame):
|
||||||
await self.add_word_timestamps([("Reset", 0)])
|
await self.add_word_timestamps([("Reset", 0)])
|
||||||
|
|
||||||
def _calculate_word_times(self, timestamp_info: Dict[str, Any]) -> List[Tuple[str, float]]:
|
def _calculate_word_times(self, timestamp_info: Dict[str, Any]) -> List[Tuple[str, float]]:
|
||||||
"""Calculate word timestamps from Inworld WebSocket API response.
|
"""Calculate word timestamps from Inworld WebSocket API response.
|
||||||
|
|
||||||
|
Adds cumulative time offset to maintain monotonically increasing timestamps
|
||||||
|
across multiple generations within an agent turn. Also tracks the generation
|
||||||
|
end time for updating cumulative time on flush.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
timestamp_info: The timestamp information from Inworld API.
|
timestamp_info: The timestamp information from Inworld API.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A list of (word, timestamp) tuples.
|
List of (word, timestamp) tuples with cumulative offset applied.
|
||||||
"""
|
"""
|
||||||
word_times: List[Tuple[str, float]] = []
|
word_times: List[Tuple[str, float]] = []
|
||||||
|
|
||||||
alignment = timestamp_info.get("wordAlignment", {})
|
alignment = timestamp_info.get("wordAlignment", {})
|
||||||
words = alignment.get("words", [])
|
words = alignment.get("words", [])
|
||||||
start_times = alignment.get("wordStartTimeSeconds", [])
|
start_times = alignment.get("wordStartTimeSeconds", [])
|
||||||
|
end_times = alignment.get("wordEndTimeSeconds", [])
|
||||||
|
|
||||||
if words and start_times and len(words) == len(start_times):
|
if words and start_times and len(words) == len(start_times):
|
||||||
for i, word in enumerate(words):
|
for i, word in enumerate(words):
|
||||||
word_times.append((word, start_times[i]))
|
word_start = self._cumulative_time + start_times[i]
|
||||||
|
word_times.append((word, word_start))
|
||||||
|
|
||||||
|
# Track cumulative end time for this generation
|
||||||
|
if end_times and len(end_times) > 0:
|
||||||
|
self._generation_end_time = self._cumulative_time + end_times[-1]
|
||||||
|
|
||||||
|
logger.trace(
|
||||||
|
f"{self}: Word timestamps - raw_start_times={start_times}, "
|
||||||
|
f"cumulative_offset={self._cumulative_time}, "
|
||||||
|
f"adjusted_times={[t for _, t in word_times]}, "
|
||||||
|
f"generation_end_time={self._generation_end_time}"
|
||||||
|
)
|
||||||
|
|
||||||
return word_times
|
return word_times
|
||||||
|
|
||||||
@@ -604,6 +635,8 @@ class InworldTTSService(AudioContextWordTTSService):
|
|||||||
|
|
||||||
self._context_id = None
|
self._context_id = None
|
||||||
self._started = False
|
self._started = False
|
||||||
|
self._cumulative_time = 0.0
|
||||||
|
self._generation_end_time = 0.0
|
||||||
logger.trace(f"{self}: Interruption handled, context reset to None")
|
logger.trace(f"{self}: Interruption handled, context reset to None")
|
||||||
|
|
||||||
def _get_websocket(self):
|
def _get_websocket(self):
|
||||||
@@ -693,6 +726,8 @@ class InworldTTSService(AudioContextWordTTSService):
|
|||||||
self._started = False
|
self._started = False
|
||||||
self._context_id = None
|
self._context_id = None
|
||||||
self._websocket = None
|
self._websocket = None
|
||||||
|
self._cumulative_time = 0.0
|
||||||
|
self._generation_end_time = 0.0
|
||||||
await self._call_event_handler("on_disconnected")
|
await self._call_event_handler("on_disconnected")
|
||||||
|
|
||||||
async def _receive_messages(self):
|
async def _receive_messages(self):
|
||||||
@@ -779,9 +814,13 @@ class InworldTTSService(AudioContextWordTTSService):
|
|||||||
if "contextCreated" in result:
|
if "contextCreated" in result:
|
||||||
logger.trace(f"{self}: Context created on server: {ctx_id}")
|
logger.trace(f"{self}: Context created on server: {ctx_id}")
|
||||||
|
|
||||||
# Handle flush completion - context is still valid, just acknowledge it
|
# Handle flush completion, which indicates the end of a generation
|
||||||
if "flushCompleted" in result:
|
if "flushCompleted" in result:
|
||||||
logger.trace(f"{self}: Flush completed for context {ctx_id}")
|
logger.trace(
|
||||||
|
f"{self}: Generation completed - updating cumulative_time: "
|
||||||
|
f"{self._cumulative_time} -> {self._generation_end_time}"
|
||||||
|
)
|
||||||
|
self._cumulative_time = self._generation_end_time
|
||||||
|
|
||||||
# Handle context closed - context no longer exists on server
|
# Handle context closed - context no longer exists on server
|
||||||
if "contextClosed" in result:
|
if "contextClosed" in result:
|
||||||
|
|||||||
Reference in New Issue
Block a user