turn on/off openai vad

This commit is contained in:
Kwindla Hultman Kramer
2024-10-07 22:09:18 -07:00
parent 3a2fbc2b19
commit 31916ed9fd
3 changed files with 41 additions and 13 deletions

View File

@@ -20,13 +20,12 @@ from pipecat.processors.aggregators.openai_llm_context import (
OpenAILLMContext, OpenAILLMContext,
) )
from pipecat.services.openai_realtime_beta import ( from pipecat.services.openai_realtime_beta import (
InputAudioTranscription,
OpenAILLMServiceRealtimeBeta, OpenAILLMServiceRealtimeBeta,
SessionProperties, SessionProperties,
TurnDetection,
) )
from pipecat.transports.services.daily import DailyParams, DailyTransport from pipecat.transports.services.daily import DailyParams, DailyTransport
from pipecat.vad.silero import SileroVADAnalyzer from pipecat.vad.silero import SileroVADAnalyzer
from pipecat.vad.vad_analyzer import VADParams
load_dotenv(override=True) load_dotenv(override=True)
@@ -76,15 +75,19 @@ async def main():
audio_out_enabled=True, audio_out_enabled=True,
audio_out_sample_rate=24000, audio_out_sample_rate=24000,
transcription_enabled=False, transcription_enabled=False,
vad_enabled=False, vad_enabled=True,
vad_analyzer=SileroVADAnalyzer(), vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
vad_audio_passthrough=True, vad_audio_passthrough=True,
), ),
) )
session_properties = SessionProperties( session_properties = SessionProperties(
input_audio_transcription=InputAudioTranscription(), # input_audio_transcription=InputAudioTranscription(),
turn_detection=TurnDetection(silence_duration_ms=1000), # Set openai TurnDetection parameters. Not setting this at all will turn it
# on by default
# turn_detection=TurnDetection(silence_duration_ms=1000),
# Or set to False to disable openai turn detection and use transport VAD
turn_detection=False,
tools=tools, tools=tools,
instructions=""" instructions="""
Your knowledge cutoff is 2023-10. You are a helpful and friendly AI. Your knowledge cutoff is 2023-10. You are a helpful and friendly AI.

View File

@@ -2,6 +2,7 @@ import json
import uuid import uuid
from typing import Any, Dict, List, Literal, Optional, Union from typing import Any, Dict, List, Literal, Optional, Union
from loguru import logger
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
# #
@@ -27,7 +28,8 @@ class SessionProperties(BaseModel):
input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None
output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None
input_audio_transcription: Optional[InputAudioTranscription] = None input_audio_transcription: Optional[InputAudioTranscription] = None
turn_detection: Optional[TurnDetection] = None # set turn_detection to False to disable turn detection
turn_detection: Optional[Union[TurnDetection, bool]] = Field(default=None)
tools: Optional[List[Dict]] = None tools: Optional[List[Dict]] = None
tool_choice: Optional[Literal["auto", "none", "required"]] = None tool_choice: Optional[Literal["auto", "none", "required"]] = None
temperature: Optional[float] = None temperature: Optional[float] = None
@@ -100,6 +102,17 @@ class SessionUpdateEvent(ClientEvent):
type: Literal["session.update"] = "session.update" type: Literal["session.update"] = "session.update"
session: SessionProperties session: SessionProperties
def model_dump(self, *args, **kwargs) -> Dict[str, Any]:
logger.debug(f"!!! SessionUpdateEvent.model_dump: {self}")
dump = super().model_dump(*args, **kwargs)
# Handle turn_detection so that False is serialized as null
if "turn_detection" in dump["session"]:
if dump["session"]["turn_detection"] is False:
dump["session"]["turn_detection"] = None
return dump
class InputAudioBufferAppendEvent(ClientEvent): class InputAudioBufferAppendEvent(ClientEvent):
type: Literal["input_audio_buffer.append"] = "input_audio_buffer.append" type: Literal["input_audio_buffer.append"] = "input_audio_buffer.append"

View File

@@ -123,7 +123,7 @@ class OpenAILLMServiceRealtimeBeta(LLMService):
session_properties: events.SessionProperties = events.SessionProperties(), session_properties: events.SessionProperties = events.SessionProperties(),
start_audio_paused: bool = False, start_audio_paused: bool = False,
send_transcription_frames: bool = True, send_transcription_frames: bool = True,
send_user_started_speaking_frames: bool = True, send_user_started_speaking_frames: bool = False,
**kwargs, **kwargs,
): ):
super().__init__(base_url=base_url, **kwargs) super().__init__(base_url=base_url, **kwargs)
@@ -133,6 +133,7 @@ class OpenAILLMServiceRealtimeBeta(LLMService):
self._session_properties = session_properties self._session_properties = session_properties
self._audio_input_paused = start_audio_paused self._audio_input_paused = start_audio_paused
self._send_transcription_frames = send_transcription_frames self._send_transcription_frames = send_transcription_frames
# todo: wire _send_user_started_speaking_frames up correctly
self._send_user_started_speaking_frames = send_user_started_speaking_frames self._send_user_started_speaking_frames = send_user_started_speaking_frames
self._websocket = None self._websocket = None
self._receive_task = None self._receive_task = None
@@ -158,7 +159,7 @@ class OpenAILLMServiceRealtimeBeta(LLMService):
await self._disconnect() await self._disconnect()
async def send_client_event(self, event: events.ClientEvent): async def send_client_event(self, event: events.ClientEvent):
await self._ws_send(event.dict(exclude_none=True)) await self._ws_send(event.model_dump(exclude_none=True))
async def _ws_send(self, realtime_message): async def _ws_send(self, realtime_message):
try: try:
@@ -359,9 +360,6 @@ class OpenAILLMServiceRealtimeBeta(LLMService):
context = self._context context = self._context
messages = context.get_unsent_messages() messages = context.get_unsent_messages()
context.update_all_messages_sent() context.update_all_messages_sent()
logger.debug(
f"Sending message context updates: {context._marker} {context.get_messages_for_logging()}"
)
items = [] items = []
for m in messages: for m in messages:
@@ -401,10 +399,20 @@ class OpenAILLMServiceRealtimeBeta(LLMService):
await self.send_client_event(events.InputAudioBufferAppendEvent(audio=payload)) await self.send_client_event(events.InputAudioBufferAppendEvent(audio=payload))
async def _handle_interruption(self, frame): async def _handle_interruption(self, frame):
await self.send_client_event(events.InputAudioBufferClearEvent())
await self.send_client_event(events.ResponseCancelEvent())
await self.stop_all_metrics() await self.stop_all_metrics()
await self.push_frame(LLMFullResponseEndFrame()) await self.push_frame(LLMFullResponseEndFrame())
await self.push_frame(TTSStoppedFrame()) await self.push_frame(TTSStoppedFrame())
# todo: track whether a response is in progress and cancel it with a response.cancela nd input_audio_buffer.clear (?)
async def _handle_user_started_speaking(self, frame):
pass
async def _handle_user_stopped_speaking(self, frame):
if self._session_properties.turn_detection is None:
await self.send_client_event(events.InputAudioBufferCommitEvent())
await self.send_client_event(events.ResponseCreateEvent())
pass
async def process_frame(self, frame: Frame, direction: FrameDirection): async def process_frame(self, frame: Frame, direction: FrameDirection):
await super().process_frame(frame, direction) await super().process_frame(frame, direction)
@@ -422,6 +430,10 @@ class OpenAILLMServiceRealtimeBeta(LLMService):
await self._send_user_audio(frame) await self._send_user_audio(frame)
elif isinstance(frame, StartInterruptionFrame): elif isinstance(frame, StartInterruptionFrame):
await self._handle_interruption(frame) await self._handle_interruption(frame)
elif isinstance(frame, UserStartedSpeakingFrame):
await self._handle_user_started_speaking(frame)
elif isinstance(frame, UserStoppedSpeakingFrame):
await self._handle_user_stopped_speaking(frame)
elif isinstance(frame, _InternalMessagesUpdateFrame): elif isinstance(frame, _InternalMessagesUpdateFrame):
self._context = frame.context self._context = frame.context
await self._send_messages_context_update() await self._send_messages_context_update()