dv-pipecat-ai 0.0.82.dev815__py3-none-any.whl → 0.0.82.dev857__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of dv-pipecat-ai might be problematic. Click here for more details.

Files changed (106) hide show
  1. {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/METADATA +8 -3
  2. {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/RECORD +106 -79
  3. pipecat/adapters/base_llm_adapter.py +44 -6
  4. pipecat/adapters/services/anthropic_adapter.py +302 -2
  5. pipecat/adapters/services/aws_nova_sonic_adapter.py +40 -2
  6. pipecat/adapters/services/bedrock_adapter.py +40 -2
  7. pipecat/adapters/services/gemini_adapter.py +276 -6
  8. pipecat/adapters/services/open_ai_adapter.py +88 -7
  9. pipecat/adapters/services/open_ai_realtime_adapter.py +39 -1
  10. pipecat/audio/dtmf/__init__.py +0 -0
  11. pipecat/audio/dtmf/types.py +47 -0
  12. pipecat/audio/dtmf/utils.py +70 -0
  13. pipecat/audio/filters/aic_filter.py +199 -0
  14. pipecat/audio/utils.py +9 -7
  15. pipecat/extensions/ivr/__init__.py +0 -0
  16. pipecat/extensions/ivr/ivr_navigator.py +452 -0
  17. pipecat/frames/frames.py +156 -43
  18. pipecat/pipeline/llm_switcher.py +76 -0
  19. pipecat/pipeline/parallel_pipeline.py +3 -3
  20. pipecat/pipeline/service_switcher.py +144 -0
  21. pipecat/pipeline/task.py +68 -28
  22. pipecat/pipeline/task_observer.py +10 -0
  23. pipecat/processors/aggregators/dtmf_aggregator.py +2 -2
  24. pipecat/processors/aggregators/llm_context.py +277 -0
  25. pipecat/processors/aggregators/llm_response.py +48 -15
  26. pipecat/processors/aggregators/llm_response_universal.py +840 -0
  27. pipecat/processors/aggregators/openai_llm_context.py +3 -3
  28. pipecat/processors/dtmf_aggregator.py +0 -2
  29. pipecat/processors/filters/stt_mute_filter.py +0 -2
  30. pipecat/processors/frame_processor.py +18 -11
  31. pipecat/processors/frameworks/rtvi.py +17 -10
  32. pipecat/processors/metrics/sentry.py +2 -0
  33. pipecat/runner/daily.py +137 -36
  34. pipecat/runner/run.py +1 -1
  35. pipecat/runner/utils.py +7 -7
  36. pipecat/serializers/asterisk.py +20 -4
  37. pipecat/serializers/exotel.py +1 -1
  38. pipecat/serializers/plivo.py +1 -1
  39. pipecat/serializers/telnyx.py +1 -1
  40. pipecat/serializers/twilio.py +1 -1
  41. pipecat/services/__init__.py +2 -2
  42. pipecat/services/anthropic/llm.py +113 -28
  43. pipecat/services/asyncai/tts.py +4 -0
  44. pipecat/services/aws/llm.py +82 -8
  45. pipecat/services/aws/tts.py +0 -10
  46. pipecat/services/aws_nova_sonic/aws.py +5 -0
  47. pipecat/services/cartesia/tts.py +28 -16
  48. pipecat/services/cerebras/llm.py +15 -10
  49. pipecat/services/deepgram/stt.py +8 -0
  50. pipecat/services/deepseek/llm.py +13 -8
  51. pipecat/services/fireworks/llm.py +13 -8
  52. pipecat/services/fish/tts.py +8 -6
  53. pipecat/services/gemini_multimodal_live/gemini.py +5 -0
  54. pipecat/services/gladia/config.py +7 -1
  55. pipecat/services/gladia/stt.py +23 -15
  56. pipecat/services/google/llm.py +159 -59
  57. pipecat/services/google/llm_openai.py +18 -3
  58. pipecat/services/grok/llm.py +2 -1
  59. pipecat/services/llm_service.py +38 -3
  60. pipecat/services/mem0/memory.py +2 -1
  61. pipecat/services/mistral/llm.py +5 -6
  62. pipecat/services/nim/llm.py +2 -1
  63. pipecat/services/openai/base_llm.py +88 -26
  64. pipecat/services/openai/image.py +6 -1
  65. pipecat/services/openai_realtime_beta/openai.py +5 -2
  66. pipecat/services/openpipe/llm.py +6 -8
  67. pipecat/services/perplexity/llm.py +13 -8
  68. pipecat/services/playht/tts.py +9 -6
  69. pipecat/services/rime/tts.py +1 -1
  70. pipecat/services/sambanova/llm.py +18 -13
  71. pipecat/services/sarvam/tts.py +415 -10
  72. pipecat/services/speechmatics/stt.py +2 -2
  73. pipecat/services/tavus/video.py +1 -1
  74. pipecat/services/tts_service.py +15 -5
  75. pipecat/services/vistaar/llm.py +2 -5
  76. pipecat/transports/base_input.py +32 -19
  77. pipecat/transports/base_output.py +39 -5
  78. pipecat/transports/daily/__init__.py +0 -0
  79. pipecat/transports/daily/transport.py +2371 -0
  80. pipecat/transports/daily/utils.py +410 -0
  81. pipecat/transports/livekit/__init__.py +0 -0
  82. pipecat/transports/livekit/transport.py +1042 -0
  83. pipecat/transports/network/fastapi_websocket.py +12 -546
  84. pipecat/transports/network/small_webrtc.py +12 -922
  85. pipecat/transports/network/webrtc_connection.py +9 -595
  86. pipecat/transports/network/websocket_client.py +12 -481
  87. pipecat/transports/network/websocket_server.py +12 -487
  88. pipecat/transports/services/daily.py +9 -2334
  89. pipecat/transports/services/helpers/daily_rest.py +12 -396
  90. pipecat/transports/services/livekit.py +12 -975
  91. pipecat/transports/services/tavus.py +12 -757
  92. pipecat/transports/smallwebrtc/__init__.py +0 -0
  93. pipecat/transports/smallwebrtc/connection.py +612 -0
  94. pipecat/transports/smallwebrtc/transport.py +936 -0
  95. pipecat/transports/tavus/__init__.py +0 -0
  96. pipecat/transports/tavus/transport.py +770 -0
  97. pipecat/transports/websocket/__init__.py +0 -0
  98. pipecat/transports/websocket/client.py +494 -0
  99. pipecat/transports/websocket/fastapi.py +559 -0
  100. pipecat/transports/websocket/server.py +500 -0
  101. pipecat/transports/whatsapp/__init__.py +0 -0
  102. pipecat/transports/whatsapp/api.py +345 -0
  103. pipecat/transports/whatsapp/client.py +364 -0
  104. {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/WHEEL +0 -0
  105. {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/licenses/LICENSE +0 -0
  106. {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,500 @@
1
+ #
2
+ # Copyright (c) 2024–2025, Daily
3
+ #
4
+ # SPDX-License-Identifier: BSD 2-Clause License
5
+ #
6
+
7
+ """WebSocket server transport implementation for Pipecat.
8
+
9
+ This module provides WebSocket server transport functionality for real-time
10
+ audio and data streaming, including client connection management, session
11
+ handling, and frame serialization.
12
+ """
13
+
14
+ import asyncio
15
+ import io
16
+ import time
17
+ import wave
18
+ from typing import Awaitable, Callable, Optional
19
+
20
+ from loguru import logger
21
+ from pydantic import BaseModel
22
+
23
+ from pipecat.frames.frames import (
24
+ CancelFrame,
25
+ EndFrame,
26
+ Frame,
27
+ InputAudioRawFrame,
28
+ OutputAudioRawFrame,
29
+ StartFrame,
30
+ StartInterruptionFrame,
31
+ TransportMessageFrame,
32
+ TransportMessageUrgentFrame,
33
+ )
34
+ from pipecat.processors.frame_processor import FrameDirection
35
+ from pipecat.serializers.base_serializer import FrameSerializer
36
+ from pipecat.transports.base_input import BaseInputTransport
37
+ from pipecat.transports.base_output import BaseOutputTransport
38
+ from pipecat.transports.base_transport import BaseTransport, TransportParams
39
+
40
+ try:
41
+ import websockets
42
+ from websockets.asyncio.server import serve as websocket_serve
43
+ from websockets.protocol import State
44
+ except ModuleNotFoundError as e:
45
+ logger.error(f"Exception: {e}")
46
+ logger.error("In order to use websockets, you need to `pip install pipecat-ai[websocket]`.")
47
+ raise Exception(f"Missing module: {e}")
48
+
49
+
50
+ class WebsocketServerParams(TransportParams):
51
+ """Configuration parameters for WebSocket server transport.
52
+
53
+ Parameters:
54
+ add_wav_header: Whether to add WAV headers to audio frames.
55
+ serializer: Frame serializer for message encoding/decoding.
56
+ session_timeout: Timeout in seconds for client sessions.
57
+ """
58
+
59
+ add_wav_header: bool = False
60
+ serializer: Optional[FrameSerializer] = None
61
+ session_timeout: Optional[int] = None
62
+
63
+
64
+ class WebsocketServerCallbacks(BaseModel):
65
+ """Callback functions for WebSocket server events.
66
+
67
+ Parameters:
68
+ on_client_connected: Called when a client connects to the server.
69
+ on_client_disconnected: Called when a client disconnects from the server.
70
+ on_session_timeout: Called when a client session times out.
71
+ on_websocket_ready: Called when the WebSocket server is ready to accept connections.
72
+ """
73
+
74
+ on_client_connected: Callable[[websockets.WebSocketServerProtocol], Awaitable[None]]
75
+ on_client_disconnected: Callable[[websockets.WebSocketServerProtocol], Awaitable[None]]
76
+ on_session_timeout: Callable[[websockets.WebSocketServerProtocol], Awaitable[None]]
77
+ on_websocket_ready: Callable[[], Awaitable[None]]
78
+
79
+
80
+ class WebsocketServerInputTransport(BaseInputTransport):
81
+ """WebSocket server input transport for receiving client data.
82
+
83
+ Handles incoming WebSocket connections, message processing, and client
84
+ session management including timeout monitoring and connection lifecycle.
85
+ """
86
+
87
+ def __init__(
88
+ self,
89
+ transport: BaseTransport,
90
+ host: str,
91
+ port: int,
92
+ params: WebsocketServerParams,
93
+ callbacks: WebsocketServerCallbacks,
94
+ **kwargs,
95
+ ):
96
+ """Initialize the WebSocket server input transport.
97
+
98
+ Args:
99
+ transport: The parent transport instance.
100
+ host: Host address to bind the WebSocket server to.
101
+ port: Port number to bind the WebSocket server to.
102
+ params: WebSocket server configuration parameters.
103
+ callbacks: Callback functions for WebSocket events.
104
+ **kwargs: Additional arguments passed to parent class.
105
+ """
106
+ super().__init__(params, **kwargs)
107
+
108
+ self._transport = transport
109
+ self._host = host
110
+ self._port = port
111
+ self._params = params
112
+ self._callbacks = callbacks
113
+
114
+ self._websocket: Optional[websockets.WebSocketServerProtocol] = None
115
+
116
+ self._server_task = None
117
+
118
+ # This task will monitor the websocket connection periodically.
119
+ self._monitor_task = None
120
+
121
+ self._stop_server_event = asyncio.Event()
122
+
123
+ # Whether we have seen a StartFrame already.
124
+ self._initialized = False
125
+
126
+ async def start(self, frame: StartFrame):
127
+ """Start the WebSocket server and initialize components.
128
+
129
+ Args:
130
+ frame: The start frame containing initialization parameters.
131
+ """
132
+ await super().start(frame)
133
+
134
+ if self._initialized:
135
+ return
136
+
137
+ self._initialized = True
138
+
139
+ if self._params.serializer:
140
+ await self._params.serializer.setup(frame)
141
+ if not self._server_task:
142
+ self._server_task = self.create_task(self._server_task_handler())
143
+ await self.set_transport_ready(frame)
144
+
145
+ async def stop(self, frame: EndFrame):
146
+ """Stop the WebSocket server and cleanup resources.
147
+
148
+ Args:
149
+ frame: The end frame signaling transport shutdown.
150
+ """
151
+ await super().stop(frame)
152
+ self._stop_server_event.set()
153
+ if self._monitor_task:
154
+ await self.cancel_task(self._monitor_task)
155
+ self._monitor_task = None
156
+ if self._server_task:
157
+ await self._server_task
158
+ self._server_task = None
159
+
160
+ async def cancel(self, frame: CancelFrame):
161
+ """Cancel the WebSocket server and stop all processing.
162
+
163
+ Args:
164
+ frame: The cancel frame signaling immediate cancellation.
165
+ """
166
+ await super().cancel(frame)
167
+ if self._monitor_task:
168
+ await self.cancel_task(self._monitor_task)
169
+ self._monitor_task = None
170
+ if self._server_task:
171
+ await self.cancel_task(self._server_task)
172
+ self._server_task = None
173
+
174
+ async def cleanup(self):
175
+ """Cleanup resources and parent transport."""
176
+ await super().cleanup()
177
+ await self._transport.cleanup()
178
+
179
+ async def _server_task_handler(self):
180
+ """Handle WebSocket server startup and client connections."""
181
+ logger.info(f"Starting websocket server on {self._host}:{self._port}")
182
+ async with websocket_serve(self._client_handler, self._host, self._port) as server:
183
+ await self._callbacks.on_websocket_ready()
184
+ await self._stop_server_event.wait()
185
+
186
+ async def _client_handler(self, websocket: websockets.WebSocketServerProtocol):
187
+ """Handle individual client connections and message processing."""
188
+ logger.info(f"New client connection from {websocket.remote_address}")
189
+ if self._websocket:
190
+ await self._websocket.close()
191
+ logger.warning("Only one client connected, using new connection")
192
+
193
+ self._websocket = websocket
194
+
195
+ # Notify
196
+ await self._callbacks.on_client_connected(websocket)
197
+
198
+ # Create a task to monitor the websocket connection
199
+ if not self._monitor_task and self._params.session_timeout:
200
+ self._monitor_task = self.create_task(
201
+ self._monitor_websocket(websocket, self._params.session_timeout)
202
+ )
203
+
204
+ # Handle incoming messages
205
+ try:
206
+ async for message in websocket:
207
+ if not self._params.serializer:
208
+ continue
209
+
210
+ frame = await self._params.serializer.deserialize(message)
211
+
212
+ if not frame:
213
+ continue
214
+
215
+ if isinstance(frame, InputAudioRawFrame):
216
+ await self.push_audio_frame(frame)
217
+ else:
218
+ await self.push_frame(frame)
219
+ except Exception as e:
220
+ logger.error(f"{self} exception receiving data: {e.__class__.__name__} ({e})")
221
+
222
+ # Notify disconnection
223
+ await self._callbacks.on_client_disconnected(websocket)
224
+
225
+ await self._websocket.close()
226
+ self._websocket = None
227
+
228
+ logger.info(f"Client {websocket.remote_address} disconnected")
229
+
230
+ async def _monitor_websocket(
231
+ self, websocket: websockets.WebSocketServerProtocol, session_timeout: int
232
+ ):
233
+ """Monitor WebSocket connection for session timeout."""
234
+ try:
235
+ await asyncio.sleep(session_timeout)
236
+ if websocket.state is not State.CLOSED:
237
+ await self._callbacks.on_session_timeout(websocket)
238
+ except asyncio.CancelledError:
239
+ logger.info(f"Monitoring task cancelled for: {websocket.remote_address}")
240
+ raise
241
+
242
+
243
+ class WebsocketServerOutputTransport(BaseOutputTransport):
244
+ """WebSocket server output transport for sending data to clients.
245
+
246
+ Handles outgoing frame serialization, audio streaming with timing control,
247
+ and client connection management for WebSocket communication.
248
+ """
249
+
250
+ def __init__(self, transport: BaseTransport, params: WebsocketServerParams, **kwargs):
251
+ """Initialize the WebSocket server output transport.
252
+
253
+ Args:
254
+ transport: The parent transport instance.
255
+ params: WebSocket server configuration parameters.
256
+ **kwargs: Additional arguments passed to parent class.
257
+ """
258
+ super().__init__(params, **kwargs)
259
+
260
+ self._transport = transport
261
+ self._params = params
262
+
263
+ self._websocket: Optional[websockets.WebSocketServerProtocol] = None
264
+
265
+ # write_audio_frame() is called quickly, as soon as we get audio
266
+ # (e.g. from the TTS), and since this is just a network connection we
267
+ # would be sending it to quickly. Instead, we want to block to emulate
268
+ # an audio device, this is what the send interval is. It will be
269
+ # computed on StartFrame.
270
+ self._send_interval = 0
271
+ self._next_send_time = 0
272
+
273
+ # Whether we have seen a StartFrame already.
274
+ self._initialized = False
275
+
276
+ async def set_client_connection(self, websocket: Optional[websockets.WebSocketServerProtocol]):
277
+ """Set the active client WebSocket connection.
278
+
279
+ Args:
280
+ websocket: The WebSocket connection to set as active, or None to clear.
281
+ """
282
+ if self._websocket:
283
+ await self._websocket.close()
284
+ logger.warning("Only one client allowed, using new connection")
285
+ self._websocket = websocket
286
+
287
+ async def start(self, frame: StartFrame):
288
+ """Start the output transport and initialize components.
289
+
290
+ Args:
291
+ frame: The start frame containing initialization parameters.
292
+ """
293
+ await super().start(frame)
294
+
295
+ if self._initialized:
296
+ return
297
+
298
+ self._initialized = True
299
+
300
+ if self._params.serializer:
301
+ await self._params.serializer.setup(frame)
302
+ self._send_interval = (self.audio_chunk_size / self.sample_rate) / 2
303
+ await self.set_transport_ready(frame)
304
+
305
+ async def stop(self, frame: EndFrame):
306
+ """Stop the output transport and send final frame.
307
+
308
+ Args:
309
+ frame: The end frame signaling transport shutdown.
310
+ """
311
+ await super().stop(frame)
312
+ await self._write_frame(frame)
313
+
314
+ async def cancel(self, frame: CancelFrame):
315
+ """Cancel the output transport and send cancellation frame.
316
+
317
+ Args:
318
+ frame: The cancel frame signaling immediate cancellation.
319
+ """
320
+ await super().cancel(frame)
321
+ await self._write_frame(frame)
322
+
323
+ async def cleanup(self):
324
+ """Cleanup resources and parent transport."""
325
+ await super().cleanup()
326
+ await self._transport.cleanup()
327
+
328
+ async def process_frame(self, frame: Frame, direction: FrameDirection):
329
+ """Process frames and handle interruption timing.
330
+
331
+ Args:
332
+ frame: The frame to process.
333
+ direction: The direction of frame flow in the pipeline.
334
+ """
335
+ await super().process_frame(frame, direction)
336
+
337
+ if isinstance(frame, StartInterruptionFrame):
338
+ await self._write_frame(frame)
339
+ self._next_send_time = 0
340
+
341
+ async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame):
342
+ """Send a transport message frame to the client.
343
+
344
+ Args:
345
+ frame: The transport message frame to send.
346
+ """
347
+ await self._write_frame(frame)
348
+
349
+ async def write_audio_frame(self, frame: OutputAudioRawFrame):
350
+ """Write an audio frame to the WebSocket client with timing control.
351
+
352
+ Args:
353
+ frame: The output audio frame to write.
354
+ """
355
+ if not self._websocket:
356
+ return
357
+
358
+ frame = OutputAudioRawFrame(
359
+ audio=frame.audio,
360
+ sample_rate=self.sample_rate,
361
+ num_channels=self._params.audio_out_channels,
362
+ )
363
+
364
+ if self._params.add_wav_header:
365
+ with io.BytesIO() as buffer:
366
+ with wave.open(buffer, "wb") as wf:
367
+ wf.setsampwidth(2)
368
+ wf.setnchannels(frame.num_channels)
369
+ wf.setframerate(frame.sample_rate)
370
+ wf.writeframes(frame.audio)
371
+ wav_frame = OutputAudioRawFrame(
372
+ buffer.getvalue(),
373
+ sample_rate=frame.sample_rate,
374
+ num_channels=frame.num_channels,
375
+ )
376
+ frame = wav_frame
377
+
378
+ await self._write_frame(frame)
379
+
380
+ # Simulate audio playback with a sleep.
381
+ await self._write_audio_sleep()
382
+
383
+ async def _write_frame(self, frame: Frame):
384
+ """Serialize and send a frame to the WebSocket client."""
385
+ if not self._params.serializer:
386
+ return
387
+
388
+ try:
389
+ payload = await self._params.serializer.serialize(frame)
390
+ if payload and self._websocket:
391
+ await self._websocket.send(payload)
392
+ except Exception as e:
393
+ logger.error(f"{self} exception sending data: {e.__class__.__name__} ({e})")
394
+
395
+ async def _write_audio_sleep(self):
396
+ """Simulate audio device timing by sleeping between audio chunks."""
397
+ # Simulate a clock.
398
+ current_time = time.monotonic()
399
+ sleep_duration = max(0, self._next_send_time - current_time)
400
+ await asyncio.sleep(sleep_duration)
401
+ if sleep_duration == 0:
402
+ self._next_send_time = time.monotonic() + self._send_interval
403
+ else:
404
+ self._next_send_time += self._send_interval
405
+
406
+
407
+ class WebsocketServerTransport(BaseTransport):
408
+ """WebSocket server transport for bidirectional real-time communication.
409
+
410
+ Provides a complete WebSocket server implementation with separate input and
411
+ output transports, client connection management, and event handling for
412
+ real-time audio and data streaming applications.
413
+ """
414
+
415
+ def __init__(
416
+ self,
417
+ params: WebsocketServerParams,
418
+ host: str = "localhost",
419
+ port: int = 8765,
420
+ input_name: Optional[str] = None,
421
+ output_name: Optional[str] = None,
422
+ ):
423
+ """Initialize the WebSocket server transport.
424
+
425
+ Args:
426
+ params: WebSocket server configuration parameters.
427
+ host: Host address to bind the server to. Defaults to "localhost".
428
+ port: Port number to bind the server to. Defaults to 8765.
429
+ input_name: Optional name for the input processor.
430
+ output_name: Optional name for the output processor.
431
+ """
432
+ super().__init__(input_name=input_name, output_name=output_name)
433
+ self._host = host
434
+ self._port = port
435
+ self._params = params
436
+
437
+ self._callbacks = WebsocketServerCallbacks(
438
+ on_client_connected=self._on_client_connected,
439
+ on_client_disconnected=self._on_client_disconnected,
440
+ on_session_timeout=self._on_session_timeout,
441
+ on_websocket_ready=self._on_websocket_ready,
442
+ )
443
+ self._input: Optional[WebsocketServerInputTransport] = None
444
+ self._output: Optional[WebsocketServerOutputTransport] = None
445
+ self._websocket: Optional[websockets.WebSocketServerProtocol] = None
446
+
447
+ # Register supported handlers. The user will only be able to register
448
+ # these handlers.
449
+ self._register_event_handler("on_client_connected")
450
+ self._register_event_handler("on_client_disconnected")
451
+ self._register_event_handler("on_session_timeout")
452
+ self._register_event_handler("on_websocket_ready")
453
+
454
+ def input(self) -> WebsocketServerInputTransport:
455
+ """Get the input transport for receiving client data.
456
+
457
+ Returns:
458
+ The WebSocket server input transport instance.
459
+ """
460
+ if not self._input:
461
+ self._input = WebsocketServerInputTransport(
462
+ self, self._host, self._port, self._params, self._callbacks, name=self._input_name
463
+ )
464
+ return self._input
465
+
466
+ def output(self) -> WebsocketServerOutputTransport:
467
+ """Get the output transport for sending data to clients.
468
+
469
+ Returns:
470
+ The WebSocket server output transport instance.
471
+ """
472
+ if not self._output:
473
+ self._output = WebsocketServerOutputTransport(
474
+ self, self._params, name=self._output_name
475
+ )
476
+ return self._output
477
+
478
+ async def _on_client_connected(self, websocket):
479
+ """Handle client connection events."""
480
+ if self._output:
481
+ await self._output.set_client_connection(websocket)
482
+ await self._call_event_handler("on_client_connected", websocket)
483
+ else:
484
+ logger.error("A WebsocketServerTransport output is missing in the pipeline")
485
+
486
+ async def _on_client_disconnected(self, websocket):
487
+ """Handle client disconnection events."""
488
+ if self._output:
489
+ await self._output.set_client_connection(None)
490
+ await self._call_event_handler("on_client_disconnected", websocket)
491
+ else:
492
+ logger.error("A WebsocketServerTransport output is missing in the pipeline")
493
+
494
+ async def _on_session_timeout(self, websocket):
495
+ """Handle client session timeout events."""
496
+ await self._call_event_handler("on_session_timeout", websocket)
497
+
498
+ async def _on_websocket_ready(self):
499
+ """Handle WebSocket server ready events."""
500
+ await self._call_event_handler("on_websocket_ready")
File without changes