@@ -15,6 +15,7 @@ from pipecat.services.deepgram import DeepgramSTTService
|
|||||||
from pipecat.services.elevenlabs import ElevenLabsTTSService
|
from pipecat.services.elevenlabs import ElevenLabsTTSService
|
||||||
from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketTransport, FastAPIWebsocketParams
|
from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketTransport, FastAPIWebsocketParams
|
||||||
from pipecat.vad.silero import SileroVADAnalyzer
|
from pipecat.vad.silero import SileroVADAnalyzer
|
||||||
|
from pipecat.serializers.twilio import TwilioFrameSerializer
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ logger.remove(0)
|
|||||||
logger.add(sys.stderr, level="DEBUG")
|
logger.add(sys.stderr, level="DEBUG")
|
||||||
|
|
||||||
|
|
||||||
async def run_bot(websocket_client):
|
async def run_bot(websocket_client, stream_sid):
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
transport = FastAPIWebsocketTransport(
|
transport = FastAPIWebsocketTransport(
|
||||||
websocket=websocket_client,
|
websocket=websocket_client,
|
||||||
@@ -34,7 +35,8 @@ async def run_bot(websocket_client):
|
|||||||
add_wav_header=False,
|
add_wav_header=False,
|
||||||
vad_enabled=True,
|
vad_enabled=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_audio_passthrough=True
|
vad_audio_passthrough=True,
|
||||||
|
serializer=TwilioFrameSerializer(stream_sid)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
from fastapi import FastAPI, WebSocket
|
from fastapi import FastAPI, WebSocket
|
||||||
@@ -26,8 +28,13 @@ async def start_call():
|
|||||||
@app.websocket("/ws")
|
@app.websocket("/ws")
|
||||||
async def websocket_endpoint(websocket: WebSocket):
|
async def websocket_endpoint(websocket: WebSocket):
|
||||||
await websocket.accept()
|
await websocket.accept()
|
||||||
|
start_data = websocket.iter_text()
|
||||||
|
await start_data.__anext__()
|
||||||
|
call_data = json.loads(await start_data.__anext__())
|
||||||
|
print(call_data, flush=True)
|
||||||
|
stream_sid = call_data['start']['streamSid']
|
||||||
print("WebSocket connection accepted")
|
print("WebSocket connection accepted")
|
||||||
await run_bot(websocket)
|
await run_bot(websocket, stream_sid)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ class TwilioFrameSerializer(FrameSerializer):
|
|||||||
AudioRawFrame: "audio",
|
AudioRawFrame: "audio",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, stream_sid: str):
|
||||||
self._sid = None
|
self._stream_sid = stream_sid
|
||||||
|
|
||||||
def serialize(self, frame: Frame) -> str | bytes | None:
|
def serialize(self, frame: Frame) -> str | bytes | None:
|
||||||
if not isinstance(frame, AudioRawFrame):
|
if not isinstance(frame, AudioRawFrame):
|
||||||
@@ -30,7 +30,7 @@ class TwilioFrameSerializer(FrameSerializer):
|
|||||||
payload = base64.b64encode(serialized_data).decode("utf-8")
|
payload = base64.b64encode(serialized_data).decode("utf-8")
|
||||||
answer = {
|
answer = {
|
||||||
"event": "media",
|
"event": "media",
|
||||||
"streamSid": self._sid,
|
"streamSid": self._stream_sid,
|
||||||
"media": {
|
"media": {
|
||||||
"payload": payload
|
"payload": payload
|
||||||
}
|
}
|
||||||
@@ -41,9 +41,6 @@ class TwilioFrameSerializer(FrameSerializer):
|
|||||||
def deserialize(self, data: str | bytes) -> Frame | None:
|
def deserialize(self, data: str | bytes) -> Frame | None:
|
||||||
message = json.loads(data)
|
message = json.loads(data)
|
||||||
|
|
||||||
if not self._sid:
|
|
||||||
self._sid = message["streamSid"] if "streamSid" in message else None
|
|
||||||
|
|
||||||
if message["event"] != "media":
|
if message["event"] != "media":
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ except ModuleNotFoundError as e:
|
|||||||
class FastAPIWebsocketParams(TransportParams):
|
class FastAPIWebsocketParams(TransportParams):
|
||||||
add_wav_header: bool = False
|
add_wav_header: bool = False
|
||||||
audio_frame_size: int = 6400 # 200ms
|
audio_frame_size: int = 6400 # 200ms
|
||||||
serializer: FrameSerializer = TwilioFrameSerializer()
|
serializer: FrameSerializer
|
||||||
|
|
||||||
|
|
||||||
class FastAPIWebsocketCallbacks(BaseModel):
|
class FastAPIWebsocketCallbacks(BaseModel):
|
||||||
@@ -125,7 +125,7 @@ class FastAPIWebsocketTransport(BaseTransport):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
websocket: WebSocket,
|
websocket: WebSocket,
|
||||||
params: FastAPIWebsocketParams = FastAPIWebsocketParams(),
|
params: FastAPIWebsocketParams,
|
||||||
input_name: str | None = None,
|
input_name: str | None = None,
|
||||||
output_name: str | None = None,
|
output_name: str | None = None,
|
||||||
loop: asyncio.AbstractEventLoop | None = None):
|
loop: asyncio.AbstractEventLoop | None = None):
|
||||||
|
|||||||
Reference in New Issue
Block a user