adding session timeout example in websocket-server example
This commit is contained in:
@@ -12,7 +12,7 @@ from dotenv import load_dotenv
|
|||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import LLMMessagesFrame
|
from pipecat.frames.frames import LLMMessagesFrame, BotInterruptionFrame, EndFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
@@ -31,6 +31,57 @@ logger.remove(0)
|
|||||||
logger.add(sys.stderr, level="DEBUG")
|
logger.add(sys.stderr, level="DEBUG")
|
||||||
|
|
||||||
|
|
||||||
|
class SessionTimeoutHandler:
|
||||||
|
"""
|
||||||
|
Handles actions to be performed when a session times out.
|
||||||
|
Inputs:
|
||||||
|
- task: Pipeline task (used to queue frames).
|
||||||
|
- tts: TTS service (used to generate speech output).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, task, tts):
|
||||||
|
self.task = task
|
||||||
|
self.tts = tts
|
||||||
|
self.background_tasks = set()
|
||||||
|
|
||||||
|
async def handle_timeout(self, client_address):
|
||||||
|
"""
|
||||||
|
Handles the timeout event for a session.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
logger.info(f"Connection timeout for {client_address}")
|
||||||
|
|
||||||
|
# Queue a BotInterruptionFrame to notify the user
|
||||||
|
await self.task.queue_frames([BotInterruptionFrame()])
|
||||||
|
|
||||||
|
# Send the TTS message to inform the user about the timeout
|
||||||
|
await self.tts.say(
|
||||||
|
"I'm sorry, we are ending the call now. Please feel free to reach out again if you need assistance."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Start the process to gracefully end the call in the background
|
||||||
|
end_call_task = asyncio.create_task(self._end_call())
|
||||||
|
self.background_tasks.add(end_call_task)
|
||||||
|
end_call_task.add_done_callback(self.background_tasks.discard)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error during session timeout handling: {e}")
|
||||||
|
|
||||||
|
async def _end_call(self):
|
||||||
|
"""
|
||||||
|
Completes the session termination process after the TTS message.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Wait for a duration to ensure TTS has completed
|
||||||
|
await asyncio.sleep(15)
|
||||||
|
|
||||||
|
# Queue both BotInterruptionFrame and EndFrame to conclude the session
|
||||||
|
await self.task.queue_frames([BotInterruptionFrame(), EndFrame()])
|
||||||
|
|
||||||
|
logger.info("TTS completed and EndFrame pushed successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error during call termination: {e}")
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
transport = WebsocketServerTransport(
|
transport = WebsocketServerTransport(
|
||||||
params=WebsocketServerParams(
|
params=WebsocketServerParams(
|
||||||
@@ -40,6 +91,7 @@ async def main():
|
|||||||
vad_enabled=True,
|
vad_enabled=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_audio_passthrough=True,
|
vad_audio_passthrough=True,
|
||||||
|
session_timeout=60 * 3, # 3 minutes
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -83,6 +135,14 @@ async def main():
|
|||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
await task.queue_frames([LLMMessagesFrame(messages)])
|
await task.queue_frames([LLMMessagesFrame(messages)])
|
||||||
|
|
||||||
|
@transport.event_handler("on_session_timeout")
|
||||||
|
async def on_session_timeout(transport, client):
|
||||||
|
logger.info(f"Entering in timeout for {client.remote_address}")
|
||||||
|
|
||||||
|
timeout_handler = SessionTimeoutHandler(task, tts)
|
||||||
|
|
||||||
|
await timeout_handler.handle_timeout(client)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner()
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ class FastAPIWebsocketInputTransport(BaseInputTransport):
|
|||||||
|
|
||||||
async def start(self, frame: StartFrame):
|
async def start(self, frame: StartFrame):
|
||||||
await super().start(frame)
|
await super().start(frame)
|
||||||
self._monitor_websocket_task = self.get_event_loop().create_task(self._monitor_websocket())
|
if self._params.session_timeout:
|
||||||
|
self._monitor_websocket_task = self.get_event_loop().create_task(
|
||||||
|
self._monitor_websocket()
|
||||||
|
)
|
||||||
await self._callbacks.on_client_connected(self._websocket)
|
await self._callbacks.on_client_connected(self._websocket)
|
||||||
self._receive_task = self.get_event_loop().create_task(self._receive_messages())
|
self._receive_task = self.get_event_loop().create_task(self._receive_messages())
|
||||||
|
|
||||||
@@ -92,9 +95,7 @@ class FastAPIWebsocketInputTransport(BaseInputTransport):
|
|||||||
await self._callbacks.on_client_disconnected(self._websocket)
|
await self._callbacks.on_client_disconnected(self._websocket)
|
||||||
|
|
||||||
async def _monitor_websocket(self):
|
async def _monitor_websocket(self):
|
||||||
"""
|
"""Wait for self._params.session_timeout seconds, if the websocket is still open, trigger timeout event."""
|
||||||
Wait for self._params.session_timeout seconds, if the websocket is still open, trigger timeout event.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
await asyncio.sleep(self._params.session_timeout)
|
await asyncio.sleep(self._params.session_timeout)
|
||||||
await self._callbacks.on_session_timeout(self._websocket)
|
await self._callbacks.on_session_timeout(self._websocket)
|
||||||
|
|||||||
@@ -124,9 +124,7 @@ class WebsocketServerInputTransport(BaseInputTransport):
|
|||||||
logger.info(f"Client {websocket.remote_address} disconnected")
|
logger.info(f"Client {websocket.remote_address} disconnected")
|
||||||
|
|
||||||
async def _monitor_websocket(self, websocket: websockets.WebSocketServerProtocol):
|
async def _monitor_websocket(self, websocket: websockets.WebSocketServerProtocol):
|
||||||
"""
|
"""Wait for self._params.session_timeout seconds, if the websocket is still open, trigger timeout event."""
|
||||||
Wait for self._params.session_timeout seconds, if the websocket is still open, trigger timeout event.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
await asyncio.sleep(self._params.session_timeout)
|
await asyncio.sleep(self._params.session_timeout)
|
||||||
if not websocket.closed:
|
if not websocket.closed:
|
||||||
@@ -228,7 +226,7 @@ class WebsocketServerTransport(BaseTransport):
|
|||||||
self._callbacks = WebsocketServerCallbacks(
|
self._callbacks = WebsocketServerCallbacks(
|
||||||
on_client_connected=self._on_client_connected,
|
on_client_connected=self._on_client_connected,
|
||||||
on_client_disconnected=self._on_client_disconnected,
|
on_client_disconnected=self._on_client_disconnected,
|
||||||
on_session_timeout=self._on_session_timeout
|
on_session_timeout=self._on_session_timeout,
|
||||||
)
|
)
|
||||||
self._input: WebsocketServerInputTransport | None = None
|
self._input: WebsocketServerInputTransport | None = None
|
||||||
self._output: WebsocketServerOutputTransport | None = None
|
self._output: WebsocketServerOutputTransport | None = None
|
||||||
@@ -268,4 +266,3 @@ class WebsocketServerTransport(BaseTransport):
|
|||||||
|
|
||||||
async def _on_session_timeout(self, websocket):
|
async def _on_session_timeout(self, websocket):
|
||||||
await self._call_event_handler("on_session_timeout", websocket)
|
await self._call_event_handler("on_session_timeout", websocket)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user