From 2638885c62e9f0523678a8e3dd375be2457ab4db Mon Sep 17 00:00:00 2001 From: filipi87 Date: Fri, 8 May 2026 09:37:07 -0300 Subject: [PATCH] Adding support for the plain websocket transport. --- src/pipecat/runner/run.py | 41 ++++++++++++++++++++++++++++++++----- src/pipecat/runner/types.py | 4 ++++ src/pipecat/runner/utils.py | 6 ++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/pipecat/runner/run.py b/src/pipecat/runner/run.py index 1c6e53c8d..62d2c4f4e 100644 --- a/src/pipecat/runner/run.py +++ b/src/pipecat/runner/run.py @@ -209,6 +209,31 @@ async def _run_telephony_bot(websocket: WebSocket, args: argparse.Namespace): await bot_module.bot(runner_args) +async def _run_websocket_bot(websocket: WebSocket, args: argparse.Namespace): + """Run a bot for plain WebSocket transport.""" + bot_module = _get_bot_module() + + runner_args = WebSocketRunnerArguments( + websocket=websocket, + transport_type="websocket", + session_id=str(uuid.uuid4()), + ) + runner_args.cli_args = args + + await bot_module.bot(runner_args) + + +def _setup_websocket_routes(app: FastAPI, args: argparse.Namespace): + """Set up the plain WebSocket route at ``/ws-client``.""" + + @app.websocket("/ws-client") + async def websocket_client_endpoint(websocket: WebSocket): + """Handle plain WebSocket connections (non-telephony).""" + await websocket.accept() + logger.debug("Plain WebSocket connection accepted") + await _run_websocket_bot(websocket, args) + + def _configure_server_app(args: argparse.Namespace): """Configure the module-level FastAPI app with routes for all transports.""" app.add_middleware( @@ -226,6 +251,7 @@ def _configure_server_app(args: argparse.Namespace): _setup_webrtc_routes(app, args, active_sessions) _setup_daily_routes(app, args) _setup_telephony_routes(app, args) + _setup_websocket_routes(app, args) _setup_unified_start_route(app, args, active_sessions) if args.whatsapp: @@ -242,7 +268,7 @@ def _setup_unified_start_route( When ``-t`` was passed on the command line, requests for any other transport are rejected with HTTP 400. """ - ALL_TRANSPORTS = ["webrtc", "daily", *TELEPHONY_TRANSPORTS] + ALL_TRANSPORTS = ["webrtc", "daily", *TELEPHONY_TRANSPORTS, "websocket"] @app.get("/status") async def status(): @@ -261,7 +287,7 @@ def _setup_unified_start_route( iceConfig: IceConfig | None dailyRoom: str | None dailyToken: str | None - websocketUrl: str | None + wsUrl: str | None @app.post("/start") async def start_agent(request: Request): @@ -388,11 +414,16 @@ def _setup_unified_start_route( elif transport in TELEPHONY_TRANSPORTS: # Telephony: the bot starts when the provider connects to /ws. # Return the WebSocket URL so the caller knows where to point their provider. - session_id = str(uuid.uuid4()) scheme = "wss" if args.host != "localhost" else "ws" return StartBotResult( - sessionId=session_id, - websocketUrl=f"{scheme}://{args.host}:{args.port}/ws", + wsUrl=f"{scheme}://{args.host}:{args.port}/ws", + ) + + elif transport == "websocket": + # Plain WebSocket: the bot starts when the client connects to /ws-client. + scheme = "wss" if args.host != "localhost" else "ws" + return StartBotResult( + wsUrl=f"{scheme}://{args.host}:{args.port}/ws-client", ) else: diff --git a/src/pipecat/runner/types.py b/src/pipecat/runner/types.py index 70ce03f29..b6bdd0014 100644 --- a/src/pipecat/runner/types.py +++ b/src/pipecat/runner/types.py @@ -105,10 +105,14 @@ class WebSocketRunnerArguments(RunnerArguments): Parameters: websocket: WebSocket connection for audio streaming + transport_type: Transport type identifier. Set to ``"websocket"`` for plain + WebSocket connections; ``None`` triggers auto-detection from the first + telephony provider message. body: Additional request data """ websocket: WebSocket + transport_type: str | None = None @dataclass diff --git a/src/pipecat/runner/utils.py b/src/pipecat/runner/utils.py index 84316a743..196a4e667 100644 --- a/src/pipecat/runner/utils.py +++ b/src/pipecat/runner/utils.py @@ -568,6 +568,12 @@ async def create_transport( ) elif isinstance(runner_args, WebSocketRunnerArguments): + if runner_args.transport_type == "websocket": + params = _get_transport_params("websocket", transport_params) + from pipecat.transports.websocket.fastapi import FastAPIWebsocketTransport + + return FastAPIWebsocketTransport(websocket=runner_args.websocket, params=params) + # Parse once to determine the provider and get data transport_type, call_data = await parse_telephony_websocket(runner_args.websocket) params = _get_transport_params(transport_type, transport_params)