beginning of realtime impl

This commit is contained in:
Kwindla Hultman Kramer
2024-10-01 21:21:43 -07:00
parent 66a76af341
commit 16101c79c5
3 changed files with 198 additions and 2 deletions

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License
#
import asyncio
import base64
import io
import json
@@ -17,6 +18,8 @@ from PIL import Image
from pydantic import BaseModel, Field
from pipecat.frames.frames import (
CancelFrame,
EndFrame,
ErrorFrame,
Frame,
FunctionCallInProgressFrame,
@@ -25,6 +28,7 @@ from pipecat.frames.frames import (
LLMFullResponseStartFrame,
LLMMessagesFrame,
LLMUpdateSettingsFrame,
StartFrame,
StartInterruptionFrame,
TextFrame,
TTSAudioRawFrame,
@@ -56,6 +60,7 @@ try:
DefaultAsyncHttpxClient,
)
from openai.types.chat import ChatCompletionChunk, ChatCompletionMessageParam
import websockets
except ModuleNotFoundError as e:
logger.error(f"Exception: {e}")
logger.error(
@@ -63,6 +68,15 @@ except ModuleNotFoundError as e:
)
raise Exception(f"Missing module: {e}")
# websocket logger
import logging
logging.basicConfig(
format="%(message)s",
level=logging.DEBUG,
)
ValidVoice = Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
VALID_VOICES: Dict[str, ValidVoice] = {
@@ -573,3 +587,91 @@ class OpenAIAssistantContextAggregator(LLMAssistantContextAggregator):
except Exception as e:
logger.error(f"Error processing frame: {e}")
class OpenAILLMServiceRealtimeBeta(LLMService):
def __init__(
self,
*,
api_key: str,
base_url="wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01",
**kwargs,
):
super().__init__(base_url=base_url, **kwargs)
self.api_key = api_key
self.base_url = base_url
self._websocket = None
self._receive_task = None
async def start(self, frame: StartFrame):
await super().start(frame)
await self._connect()
async def stop(self, frame: EndFrame):
await super().stop(frame)
await self._disconnect()
async def cancel(self, frame: CancelFrame):
await super().cancel(frame)
await self._disconnect()
async def _connect(self):
try:
logger.debug(f"connecting to {self.base_url} with api_key {self.api_key}")
self._websocket = await websockets.connect(
uri=self.base_url,
extra_headers={
"Authorization": f"Bearer {self.api_key}",
"OpenAI-Beta": "realtime=v1",
},
)
self._receive_task = self.get_event_loop().create_task(self._receive_task_handler())
except Exception as e:
logger.error(f"{self} initialization error: {e}")
self._websocket = None
async def _disconnect(self):
pass
async def _receive_task_handler(self):
try:
async for message in self._get_websocket():
msg = json.loads(message)
logger.debug(f"Received message: {msg}")
except asyncio.CancelledError:
pass
except Exception as e:
logger.error(f"{self} exception: {e}")
async def process_frame(self, frame: Frame, direction: FrameDirection):
await super().process_frame(frame, direction)
await self.push_frame(frame, direction)
# if isinstance(frame, TranscriptionFrame):
# self._websocket.send(
# json.dumps(
# {
# {
# "type": "response.create",
# "response": {
# "modalities": ["text"],
# "instructions": frame.text,
# },
# }
# }
# )
# )
# async def get_chat_completions(
# self, context: OpenAILLMContext, messages: List[ChatCompletionMessageParam]
# ) -> AsyncStream[ChatCompletionChunk]:
# async def _empty_async_generator() -> AsyncGenerator[str, None]:
# try:
# if False:
# yield ""
# except asyncio.CancelledError:
# return
# except Exception as e:
# logger.error(f"{self} exception: {e}")
# return _empty_async_generator()