Spaces:
Running
Running
| # SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: BSD 2-Clause License | |
| """User presence detection and greeting processor for ACE conversation pipeline.""" | |
| from loguru import logger | |
| from pipecat.frames.frames import ( | |
| ControlFrame, | |
| Frame, | |
| InputAudioRawFrame, | |
| StartInterruptionFrame, | |
| SystemFrame, | |
| TTSSpeakFrame, | |
| UserStartedSpeakingFrame, | |
| ) | |
| from pipecat.processors.frame_processor import FrameDirection, FrameProcessor | |
| from nvidia_pipecat.frames.action import FinishedPresenceUserActionFrame, StartedPresenceUserActionFrame | |
| class UserPresenceProcesssor(FrameProcessor): | |
| """Manages user presence detection and automated greetings in the ACE conversation pipeline. | |
| Manages automated greetings and pipeline activation based on user presence state. | |
| Sends welcome/farewell messages and controls frame forwarding accordingly. | |
| Input Frames: | |
| StartedPresenceUserActionFrame: User becomes present | |
| FinishedPresenceUserActionFrame: User leaves | |
| UserStartedSpeakingFrame: User speech event | |
| InputAudioRawFrame: Raw audio input | |
| SystemFrame: System events | |
| ControlFrame: Control events | |
| """ | |
| def __init__(self, welcome_msg="Hello", farewell_msg="Goodbye", **kwargs): | |
| """Initialize the user presence processor. | |
| Args: | |
| welcome_msg (str): Message spoken when user presence detected. | |
| farewell_msg (str): Message spoken when user leaves. | |
| **kwargs: Additional arguments passed to parent FrameProcessor. | |
| """ | |
| super().__init__(**kwargs) | |
| self._welcome_msg = welcome_msg | |
| self._farewell_msg = farewell_msg | |
| self._is_user_present = False # At startup, it is assumed that the user is not present | |
| async def _greet_welcome(self): | |
| """Internal method to handle welcome greeting.""" | |
| logger.debug("User detected. Greeting a welcome") | |
| try: | |
| await self.push_frame(TTSSpeakFrame(self._welcome_msg)) | |
| self._is_user_present = True | |
| except Exception as e: | |
| logger.error(e) | |
| async def _greet_farewell(self): | |
| """Internal method to handle farewell greeting.""" | |
| logger.debug(f"User left. Bidding farewell. {self._farewell_msg}") | |
| try: | |
| await self.push_frame(TTSSpeakFrame(self._farewell_msg)) | |
| self._is_user_present = False | |
| except Exception as e: | |
| logger.error(e) | |
| async def process_frame(self, frame: Frame, direction: FrameDirection): | |
| """Process incoming frames and manage user presence state. | |
| Handles user presence detection, greeting messages, and controls frame forwarding | |
| based on user presence state. | |
| Args: | |
| frame (Frame): Incoming frame to process. | |
| direction (FrameDirection): Frame flow direction. | |
| """ | |
| await super().process_frame(frame, direction) | |
| if isinstance(frame, StartedPresenceUserActionFrame): | |
| # Welcome the user on detecting presence | |
| await self.push_frame(frame, direction) | |
| await self._greet_welcome() | |
| elif isinstance(frame, FinishedPresenceUserActionFrame): | |
| # Greet farewell to the user on detecting absence. | |
| await self.push_frame(StartInterruptionFrame()) | |
| await self.push_frame(frame, direction) | |
| await self._greet_farewell() | |
| elif isinstance(frame, SystemFrame | ControlFrame): | |
| if isinstance(frame, UserStartedSpeakingFrame | InputAudioRawFrame) and not self._is_user_present: | |
| return | |
| else: | |
| await self.push_frame(frame, direction) | |
| else: | |
| # Pass all frames if user is present | |
| if self._is_user_present: | |
| await self.push_frame(frame, direction) | |
| else: | |
| logger.debug(f"Frame {frame} blocked as no user is present") | |