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.
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/METADATA +8 -3
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/RECORD +106 -79
- pipecat/adapters/base_llm_adapter.py +44 -6
- pipecat/adapters/services/anthropic_adapter.py +302 -2
- pipecat/adapters/services/aws_nova_sonic_adapter.py +40 -2
- pipecat/adapters/services/bedrock_adapter.py +40 -2
- pipecat/adapters/services/gemini_adapter.py +276 -6
- pipecat/adapters/services/open_ai_adapter.py +88 -7
- pipecat/adapters/services/open_ai_realtime_adapter.py +39 -1
- pipecat/audio/dtmf/__init__.py +0 -0
- pipecat/audio/dtmf/types.py +47 -0
- pipecat/audio/dtmf/utils.py +70 -0
- pipecat/audio/filters/aic_filter.py +199 -0
- pipecat/audio/utils.py +9 -7
- pipecat/extensions/ivr/__init__.py +0 -0
- pipecat/extensions/ivr/ivr_navigator.py +452 -0
- pipecat/frames/frames.py +156 -43
- pipecat/pipeline/llm_switcher.py +76 -0
- pipecat/pipeline/parallel_pipeline.py +3 -3
- pipecat/pipeline/service_switcher.py +144 -0
- pipecat/pipeline/task.py +68 -28
- pipecat/pipeline/task_observer.py +10 -0
- pipecat/processors/aggregators/dtmf_aggregator.py +2 -2
- pipecat/processors/aggregators/llm_context.py +277 -0
- pipecat/processors/aggregators/llm_response.py +48 -15
- pipecat/processors/aggregators/llm_response_universal.py +840 -0
- pipecat/processors/aggregators/openai_llm_context.py +3 -3
- pipecat/processors/dtmf_aggregator.py +0 -2
- pipecat/processors/filters/stt_mute_filter.py +0 -2
- pipecat/processors/frame_processor.py +18 -11
- pipecat/processors/frameworks/rtvi.py +17 -10
- pipecat/processors/metrics/sentry.py +2 -0
- pipecat/runner/daily.py +137 -36
- pipecat/runner/run.py +1 -1
- pipecat/runner/utils.py +7 -7
- pipecat/serializers/asterisk.py +20 -4
- pipecat/serializers/exotel.py +1 -1
- pipecat/serializers/plivo.py +1 -1
- pipecat/serializers/telnyx.py +1 -1
- pipecat/serializers/twilio.py +1 -1
- pipecat/services/__init__.py +2 -2
- pipecat/services/anthropic/llm.py +113 -28
- pipecat/services/asyncai/tts.py +4 -0
- pipecat/services/aws/llm.py +82 -8
- pipecat/services/aws/tts.py +0 -10
- pipecat/services/aws_nova_sonic/aws.py +5 -0
- pipecat/services/cartesia/tts.py +28 -16
- pipecat/services/cerebras/llm.py +15 -10
- pipecat/services/deepgram/stt.py +8 -0
- pipecat/services/deepseek/llm.py +13 -8
- pipecat/services/fireworks/llm.py +13 -8
- pipecat/services/fish/tts.py +8 -6
- pipecat/services/gemini_multimodal_live/gemini.py +5 -0
- pipecat/services/gladia/config.py +7 -1
- pipecat/services/gladia/stt.py +23 -15
- pipecat/services/google/llm.py +159 -59
- pipecat/services/google/llm_openai.py +18 -3
- pipecat/services/grok/llm.py +2 -1
- pipecat/services/llm_service.py +38 -3
- pipecat/services/mem0/memory.py +2 -1
- pipecat/services/mistral/llm.py +5 -6
- pipecat/services/nim/llm.py +2 -1
- pipecat/services/openai/base_llm.py +88 -26
- pipecat/services/openai/image.py +6 -1
- pipecat/services/openai_realtime_beta/openai.py +5 -2
- pipecat/services/openpipe/llm.py +6 -8
- pipecat/services/perplexity/llm.py +13 -8
- pipecat/services/playht/tts.py +9 -6
- pipecat/services/rime/tts.py +1 -1
- pipecat/services/sambanova/llm.py +18 -13
- pipecat/services/sarvam/tts.py +415 -10
- pipecat/services/speechmatics/stt.py +2 -2
- pipecat/services/tavus/video.py +1 -1
- pipecat/services/tts_service.py +15 -5
- pipecat/services/vistaar/llm.py +2 -5
- pipecat/transports/base_input.py +32 -19
- pipecat/transports/base_output.py +39 -5
- pipecat/transports/daily/__init__.py +0 -0
- pipecat/transports/daily/transport.py +2371 -0
- pipecat/transports/daily/utils.py +410 -0
- pipecat/transports/livekit/__init__.py +0 -0
- pipecat/transports/livekit/transport.py +1042 -0
- pipecat/transports/network/fastapi_websocket.py +12 -546
- pipecat/transports/network/small_webrtc.py +12 -922
- pipecat/transports/network/webrtc_connection.py +9 -595
- pipecat/transports/network/websocket_client.py +12 -481
- pipecat/transports/network/websocket_server.py +12 -487
- pipecat/transports/services/daily.py +9 -2334
- pipecat/transports/services/helpers/daily_rest.py +12 -396
- pipecat/transports/services/livekit.py +12 -975
- pipecat/transports/services/tavus.py +12 -757
- pipecat/transports/smallwebrtc/__init__.py +0 -0
- pipecat/transports/smallwebrtc/connection.py +612 -0
- pipecat/transports/smallwebrtc/transport.py +936 -0
- pipecat/transports/tavus/__init__.py +0 -0
- pipecat/transports/tavus/transport.py +770 -0
- pipecat/transports/websocket/__init__.py +0 -0
- pipecat/transports/websocket/client.py +494 -0
- pipecat/transports/websocket/fastapi.py +559 -0
- pipecat/transports/websocket/server.py +500 -0
- pipecat/transports/whatsapp/__init__.py +0 -0
- pipecat/transports/whatsapp/api.py +345 -0
- pipecat/transports/whatsapp/client.py +364 -0
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/WHEEL +0 -0
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/licenses/LICENSE +0 -0
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/top_level.txt +0 -0
|
@@ -11,484 +11,15 @@ communication over WebSocket connections, with support for audio streaming,
|
|
|
11
11
|
frame serialization, and connection management.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
CancelFrame,
|
|
27
|
-
EndFrame,
|
|
28
|
-
Frame,
|
|
29
|
-
InputAudioRawFrame,
|
|
30
|
-
OutputAudioRawFrame,
|
|
31
|
-
StartFrame,
|
|
32
|
-
TransportMessageFrame,
|
|
33
|
-
TransportMessageUrgentFrame,
|
|
34
|
-
)
|
|
35
|
-
from pipecat.processors.frame_processor import FrameProcessorSetup
|
|
36
|
-
from pipecat.serializers.base_serializer import FrameSerializer
|
|
37
|
-
from pipecat.serializers.protobuf import ProtobufFrameSerializer
|
|
38
|
-
from pipecat.transports.base_input import BaseInputTransport
|
|
39
|
-
from pipecat.transports.base_output import BaseOutputTransport
|
|
40
|
-
from pipecat.transports.base_transport import BaseTransport, TransportParams
|
|
41
|
-
from pipecat.utils.asyncio.task_manager import BaseTaskManager
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class WebsocketClientParams(TransportParams):
|
|
45
|
-
"""Configuration parameters for WebSocket client transport.
|
|
46
|
-
|
|
47
|
-
Parameters:
|
|
48
|
-
add_wav_header: Whether to add WAV headers to audio frames.
|
|
49
|
-
serializer: Frame serializer for encoding/decoding messages.
|
|
50
|
-
"""
|
|
51
|
-
|
|
52
|
-
add_wav_header: bool = True
|
|
53
|
-
serializer: Optional[FrameSerializer] = None
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class WebsocketClientCallbacks(BaseModel):
|
|
57
|
-
"""Callback functions for WebSocket client events.
|
|
58
|
-
|
|
59
|
-
Parameters:
|
|
60
|
-
on_connected: Called when WebSocket connection is established.
|
|
61
|
-
on_disconnected: Called when WebSocket connection is closed.
|
|
62
|
-
on_message: Called when a message is received from the WebSocket.
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
on_connected: Callable[[websockets.WebSocketClientProtocol], Awaitable[None]]
|
|
66
|
-
on_disconnected: Callable[[websockets.WebSocketClientProtocol], Awaitable[None]]
|
|
67
|
-
on_message: Callable[[websockets.WebSocketClientProtocol, websockets.Data], Awaitable[None]]
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
class WebsocketClientSession:
|
|
71
|
-
"""Manages a WebSocket client connection session.
|
|
72
|
-
|
|
73
|
-
Handles connection lifecycle, message sending/receiving, and provides
|
|
74
|
-
callback mechanisms for connection events.
|
|
75
|
-
"""
|
|
76
|
-
|
|
77
|
-
def __init__(
|
|
78
|
-
self,
|
|
79
|
-
uri: str,
|
|
80
|
-
params: WebsocketClientParams,
|
|
81
|
-
callbacks: WebsocketClientCallbacks,
|
|
82
|
-
transport_name: str,
|
|
83
|
-
):
|
|
84
|
-
"""Initialize the WebSocket client session.
|
|
85
|
-
|
|
86
|
-
Args:
|
|
87
|
-
uri: The WebSocket URI to connect to.
|
|
88
|
-
params: Configuration parameters for the session.
|
|
89
|
-
callbacks: Callback functions for session events.
|
|
90
|
-
transport_name: Name of the parent transport for logging.
|
|
91
|
-
"""
|
|
92
|
-
self._uri = uri
|
|
93
|
-
self._params = params
|
|
94
|
-
self._callbacks = callbacks
|
|
95
|
-
self._transport_name = transport_name
|
|
96
|
-
|
|
97
|
-
self._leave_counter = 0
|
|
98
|
-
self._task_manager: Optional[BaseTaskManager] = None
|
|
99
|
-
self._websocket: Optional[websockets.WebSocketClientProtocol] = None
|
|
100
|
-
|
|
101
|
-
@property
|
|
102
|
-
def task_manager(self) -> BaseTaskManager:
|
|
103
|
-
"""Get the task manager for this session.
|
|
104
|
-
|
|
105
|
-
Returns:
|
|
106
|
-
The task manager instance.
|
|
107
|
-
|
|
108
|
-
Raises:
|
|
109
|
-
Exception: If task manager is not initialized.
|
|
110
|
-
"""
|
|
111
|
-
if not self._task_manager:
|
|
112
|
-
raise Exception(
|
|
113
|
-
f"{self._transport_name}::WebsocketClientSession: TaskManager not initialized (pipeline not started?)"
|
|
114
|
-
)
|
|
115
|
-
return self._task_manager
|
|
116
|
-
|
|
117
|
-
async def setup(self, task_manager: BaseTaskManager):
|
|
118
|
-
"""Set up the session with a task manager.
|
|
119
|
-
|
|
120
|
-
Args:
|
|
121
|
-
task_manager: The task manager to use for session tasks.
|
|
122
|
-
"""
|
|
123
|
-
self._leave_counter += 1
|
|
124
|
-
if not self._task_manager:
|
|
125
|
-
self._task_manager = task_manager
|
|
126
|
-
|
|
127
|
-
async def connect(self):
|
|
128
|
-
"""Connect to the WebSocket server."""
|
|
129
|
-
if self._websocket:
|
|
130
|
-
return
|
|
131
|
-
|
|
132
|
-
try:
|
|
133
|
-
self._websocket = await websocket_connect(uri=self._uri, open_timeout=10)
|
|
134
|
-
self._client_task = self.task_manager.create_task(
|
|
135
|
-
self._client_task_handler(),
|
|
136
|
-
f"{self._transport_name}::WebsocketClientSession::_client_task_handler",
|
|
137
|
-
)
|
|
138
|
-
await self._callbacks.on_connected(self._websocket)
|
|
139
|
-
except TimeoutError:
|
|
140
|
-
logger.error(f"Timeout connecting to {self._uri}")
|
|
141
|
-
|
|
142
|
-
async def disconnect(self):
|
|
143
|
-
"""Disconnect from the WebSocket server."""
|
|
144
|
-
self._leave_counter -= 1
|
|
145
|
-
if not self._websocket or self._leave_counter > 0:
|
|
146
|
-
return
|
|
147
|
-
|
|
148
|
-
await self.task_manager.cancel_task(self._client_task)
|
|
149
|
-
|
|
150
|
-
await self._websocket.close()
|
|
151
|
-
self._websocket = None
|
|
152
|
-
|
|
153
|
-
async def send(self, message: websockets.Data):
|
|
154
|
-
"""Send a message through the WebSocket connection.
|
|
155
|
-
|
|
156
|
-
Args:
|
|
157
|
-
message: The message data to send.
|
|
158
|
-
"""
|
|
159
|
-
try:
|
|
160
|
-
if self._websocket:
|
|
161
|
-
await self._websocket.send(message)
|
|
162
|
-
except Exception as e:
|
|
163
|
-
logger.error(f"{self} exception sending data: {e.__class__.__name__} ({e})")
|
|
164
|
-
|
|
165
|
-
async def _client_task_handler(self):
|
|
166
|
-
"""Handle incoming messages from the WebSocket connection."""
|
|
167
|
-
try:
|
|
168
|
-
# Handle incoming messages
|
|
169
|
-
async for message in self._websocket:
|
|
170
|
-
await self._callbacks.on_message(self._websocket, message)
|
|
171
|
-
except Exception as e:
|
|
172
|
-
logger.error(f"{self} exception receiving data: {e.__class__.__name__} ({e})")
|
|
173
|
-
|
|
174
|
-
await self._callbacks.on_disconnected(self._websocket)
|
|
175
|
-
|
|
176
|
-
def __str__(self):
|
|
177
|
-
"""String representation of the WebSocket client session."""
|
|
178
|
-
return f"{self._transport_name}::WebsocketClientSession"
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
class WebsocketClientInputTransport(BaseInputTransport):
|
|
182
|
-
"""WebSocket client input transport for receiving frames.
|
|
183
|
-
|
|
184
|
-
Handles incoming WebSocket messages, deserializes them to frames,
|
|
185
|
-
and pushes them downstream in the processing pipeline.
|
|
186
|
-
"""
|
|
187
|
-
|
|
188
|
-
def __init__(
|
|
189
|
-
self,
|
|
190
|
-
transport: BaseTransport,
|
|
191
|
-
session: WebsocketClientSession,
|
|
192
|
-
params: WebsocketClientParams,
|
|
193
|
-
):
|
|
194
|
-
"""Initialize the WebSocket client input transport.
|
|
195
|
-
|
|
196
|
-
Args:
|
|
197
|
-
transport: The parent transport instance.
|
|
198
|
-
session: The WebSocket session to use for communication.
|
|
199
|
-
params: Configuration parameters for the transport.
|
|
200
|
-
"""
|
|
201
|
-
super().__init__(params)
|
|
202
|
-
|
|
203
|
-
self._transport = transport
|
|
204
|
-
self._session = session
|
|
205
|
-
self._params = params
|
|
206
|
-
|
|
207
|
-
# Whether we have seen a StartFrame already.
|
|
208
|
-
self._initialized = False
|
|
209
|
-
|
|
210
|
-
async def setup(self, setup: FrameProcessorSetup):
|
|
211
|
-
"""Set up the input transport with the frame processor setup.
|
|
212
|
-
|
|
213
|
-
Args:
|
|
214
|
-
setup: The frame processor setup configuration.
|
|
215
|
-
"""
|
|
216
|
-
await super().setup(setup)
|
|
217
|
-
await self._session.setup(setup.task_manager)
|
|
218
|
-
|
|
219
|
-
async def start(self, frame: StartFrame):
|
|
220
|
-
"""Start the input transport and initialize the WebSocket connection.
|
|
221
|
-
|
|
222
|
-
Args:
|
|
223
|
-
frame: The start frame containing initialization parameters.
|
|
224
|
-
"""
|
|
225
|
-
await super().start(frame)
|
|
226
|
-
|
|
227
|
-
if self._initialized:
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
self._initialized = True
|
|
231
|
-
|
|
232
|
-
if self._params.serializer:
|
|
233
|
-
await self._params.serializer.setup(frame)
|
|
234
|
-
await self._session.connect()
|
|
235
|
-
await self.set_transport_ready(frame)
|
|
236
|
-
|
|
237
|
-
async def stop(self, frame: EndFrame):
|
|
238
|
-
"""Stop the input transport and disconnect from WebSocket.
|
|
239
|
-
|
|
240
|
-
Args:
|
|
241
|
-
frame: The end frame signaling transport shutdown.
|
|
242
|
-
"""
|
|
243
|
-
await super().stop(frame)
|
|
244
|
-
await self._session.disconnect()
|
|
245
|
-
|
|
246
|
-
async def cancel(self, frame: CancelFrame):
|
|
247
|
-
"""Cancel the input transport and disconnect from WebSocket.
|
|
248
|
-
|
|
249
|
-
Args:
|
|
250
|
-
frame: The cancel frame signaling immediate cancellation.
|
|
251
|
-
"""
|
|
252
|
-
await super().cancel(frame)
|
|
253
|
-
await self._session.disconnect()
|
|
254
|
-
|
|
255
|
-
async def cleanup(self):
|
|
256
|
-
"""Clean up the input transport resources."""
|
|
257
|
-
await super().cleanup()
|
|
258
|
-
await self._transport.cleanup()
|
|
259
|
-
|
|
260
|
-
async def on_message(self, websocket, message):
|
|
261
|
-
"""Handle incoming WebSocket messages.
|
|
262
|
-
|
|
263
|
-
Args:
|
|
264
|
-
websocket: The WebSocket connection that received the message.
|
|
265
|
-
message: The received message data.
|
|
266
|
-
"""
|
|
267
|
-
if not self._params.serializer:
|
|
268
|
-
return
|
|
269
|
-
frame = await self._params.serializer.deserialize(message)
|
|
270
|
-
if not frame:
|
|
271
|
-
return
|
|
272
|
-
if isinstance(frame, InputAudioRawFrame) and self._params.audio_in_enabled:
|
|
273
|
-
await self.push_audio_frame(frame)
|
|
274
|
-
else:
|
|
275
|
-
await self.push_frame(frame)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
class WebsocketClientOutputTransport(BaseOutputTransport):
|
|
279
|
-
"""WebSocket client output transport for sending frames.
|
|
280
|
-
|
|
281
|
-
Handles outgoing frames, serializes them for WebSocket transmission,
|
|
282
|
-
and manages audio streaming with proper timing simulation.
|
|
283
|
-
"""
|
|
284
|
-
|
|
285
|
-
def __init__(
|
|
286
|
-
self,
|
|
287
|
-
transport: BaseTransport,
|
|
288
|
-
session: WebsocketClientSession,
|
|
289
|
-
params: WebsocketClientParams,
|
|
290
|
-
):
|
|
291
|
-
"""Initialize the WebSocket client output transport.
|
|
292
|
-
|
|
293
|
-
Args:
|
|
294
|
-
transport: The parent transport instance.
|
|
295
|
-
session: The WebSocket session to use for communication.
|
|
296
|
-
params: Configuration parameters for the transport.
|
|
297
|
-
"""
|
|
298
|
-
super().__init__(params)
|
|
299
|
-
|
|
300
|
-
self._transport = transport
|
|
301
|
-
self._session = session
|
|
302
|
-
self._params = params
|
|
303
|
-
|
|
304
|
-
# write_audio_frame() is called quickly, as soon as we get audio
|
|
305
|
-
# (e.g. from the TTS), and since this is just a network connection we
|
|
306
|
-
# would be sending it to quickly. Instead, we want to block to emulate
|
|
307
|
-
# an audio device, this is what the send interval is. It will be
|
|
308
|
-
# computed on StartFrame.
|
|
309
|
-
self._send_interval = 0
|
|
310
|
-
self._next_send_time = 0
|
|
311
|
-
|
|
312
|
-
# Whether we have seen a StartFrame already.
|
|
313
|
-
self._initialized = False
|
|
314
|
-
|
|
315
|
-
async def setup(self, setup: FrameProcessorSetup):
|
|
316
|
-
"""Set up the output transport with the frame processor setup.
|
|
317
|
-
|
|
318
|
-
Args:
|
|
319
|
-
setup: The frame processor setup configuration.
|
|
320
|
-
"""
|
|
321
|
-
await super().setup(setup)
|
|
322
|
-
await self._session.setup(setup.task_manager)
|
|
323
|
-
|
|
324
|
-
async def start(self, frame: StartFrame):
|
|
325
|
-
"""Start the output transport and initialize the WebSocket connection.
|
|
326
|
-
|
|
327
|
-
Args:
|
|
328
|
-
frame: The start frame containing initialization parameters.
|
|
329
|
-
"""
|
|
330
|
-
await super().start(frame)
|
|
331
|
-
|
|
332
|
-
if self._initialized:
|
|
333
|
-
return
|
|
334
|
-
|
|
335
|
-
self._initialized = True
|
|
336
|
-
|
|
337
|
-
self._send_interval = (self.audio_chunk_size / self.sample_rate) / 2
|
|
338
|
-
if self._params.serializer:
|
|
339
|
-
await self._params.serializer.setup(frame)
|
|
340
|
-
await self._session.connect()
|
|
341
|
-
await self.set_transport_ready(frame)
|
|
342
|
-
|
|
343
|
-
async def stop(self, frame: EndFrame):
|
|
344
|
-
"""Stop the output transport and disconnect from WebSocket.
|
|
345
|
-
|
|
346
|
-
Args:
|
|
347
|
-
frame: The end frame signaling transport shutdown.
|
|
348
|
-
"""
|
|
349
|
-
await super().stop(frame)
|
|
350
|
-
await self._session.disconnect()
|
|
351
|
-
|
|
352
|
-
async def cancel(self, frame: CancelFrame):
|
|
353
|
-
"""Cancel the output transport and disconnect from WebSocket.
|
|
354
|
-
|
|
355
|
-
Args:
|
|
356
|
-
frame: The cancel frame signaling immediate cancellation.
|
|
357
|
-
"""
|
|
358
|
-
await super().cancel(frame)
|
|
359
|
-
await self._session.disconnect()
|
|
360
|
-
|
|
361
|
-
async def cleanup(self):
|
|
362
|
-
"""Clean up the output transport resources."""
|
|
363
|
-
await super().cleanup()
|
|
364
|
-
await self._transport.cleanup()
|
|
365
|
-
|
|
366
|
-
async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame):
|
|
367
|
-
"""Send a transport message through the WebSocket.
|
|
368
|
-
|
|
369
|
-
Args:
|
|
370
|
-
frame: The transport message frame to send.
|
|
371
|
-
"""
|
|
372
|
-
await self._write_frame(frame)
|
|
373
|
-
|
|
374
|
-
async def write_audio_frame(self, frame: OutputAudioRawFrame):
|
|
375
|
-
"""Write an audio frame to the WebSocket with optional WAV header.
|
|
376
|
-
|
|
377
|
-
Args:
|
|
378
|
-
frame: The output audio frame to write.
|
|
379
|
-
"""
|
|
380
|
-
frame = OutputAudioRawFrame(
|
|
381
|
-
audio=frame.audio,
|
|
382
|
-
sample_rate=self.sample_rate,
|
|
383
|
-
num_channels=self._params.audio_out_channels,
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
if self._params.add_wav_header:
|
|
387
|
-
with io.BytesIO() as buffer:
|
|
388
|
-
with wave.open(buffer, "wb") as wf:
|
|
389
|
-
wf.setsampwidth(2)
|
|
390
|
-
wf.setnchannels(frame.num_channels)
|
|
391
|
-
wf.setframerate(frame.sample_rate)
|
|
392
|
-
wf.writeframes(frame.audio)
|
|
393
|
-
wav_frame = OutputAudioRawFrame(
|
|
394
|
-
buffer.getvalue(),
|
|
395
|
-
sample_rate=frame.sample_rate,
|
|
396
|
-
num_channels=frame.num_channels,
|
|
397
|
-
)
|
|
398
|
-
frame = wav_frame
|
|
399
|
-
|
|
400
|
-
await self._write_frame(frame)
|
|
401
|
-
|
|
402
|
-
# Simulate audio playback with a sleep.
|
|
403
|
-
await self._write_audio_sleep()
|
|
404
|
-
|
|
405
|
-
async def _write_frame(self, frame: Frame):
|
|
406
|
-
"""Write a frame to the WebSocket after serialization."""
|
|
407
|
-
if not self._params.serializer:
|
|
408
|
-
return
|
|
409
|
-
payload = await self._params.serializer.serialize(frame)
|
|
410
|
-
if payload:
|
|
411
|
-
await self._session.send(payload)
|
|
412
|
-
|
|
413
|
-
async def _write_audio_sleep(self):
|
|
414
|
-
"""Simulate audio playback timing with sleep delays."""
|
|
415
|
-
# Simulate a clock.
|
|
416
|
-
current_time = time.monotonic()
|
|
417
|
-
sleep_duration = max(0, self._next_send_time - current_time)
|
|
418
|
-
await asyncio.sleep(sleep_duration)
|
|
419
|
-
if sleep_duration == 0:
|
|
420
|
-
self._next_send_time = time.monotonic() + self._send_interval
|
|
421
|
-
else:
|
|
422
|
-
self._next_send_time += self._send_interval
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
class WebsocketClientTransport(BaseTransport):
|
|
426
|
-
"""WebSocket client transport for bidirectional communication.
|
|
427
|
-
|
|
428
|
-
Provides a complete WebSocket client transport implementation with
|
|
429
|
-
input and output capabilities, connection management, and event handling.
|
|
430
|
-
"""
|
|
431
|
-
|
|
432
|
-
def __init__(
|
|
433
|
-
self,
|
|
434
|
-
uri: str,
|
|
435
|
-
params: Optional[WebsocketClientParams] = None,
|
|
436
|
-
):
|
|
437
|
-
"""Initialize the WebSocket client transport.
|
|
438
|
-
|
|
439
|
-
Args:
|
|
440
|
-
uri: The WebSocket URI to connect to.
|
|
441
|
-
params: Optional configuration parameters for the transport.
|
|
442
|
-
"""
|
|
443
|
-
super().__init__()
|
|
444
|
-
|
|
445
|
-
self._params = params or WebsocketClientParams()
|
|
446
|
-
self._params.serializer = self._params.serializer or ProtobufFrameSerializer()
|
|
447
|
-
|
|
448
|
-
callbacks = WebsocketClientCallbacks(
|
|
449
|
-
on_connected=self._on_connected,
|
|
450
|
-
on_disconnected=self._on_disconnected,
|
|
451
|
-
on_message=self._on_message,
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
self._session = WebsocketClientSession(uri, self._params, callbacks, self.name)
|
|
455
|
-
self._input: Optional[WebsocketClientInputTransport] = None
|
|
456
|
-
self._output: Optional[WebsocketClientOutputTransport] = None
|
|
457
|
-
|
|
458
|
-
# Register supported handlers. The user will only be able to register
|
|
459
|
-
# these handlers.
|
|
460
|
-
self._register_event_handler("on_connected")
|
|
461
|
-
self._register_event_handler("on_disconnected")
|
|
462
|
-
|
|
463
|
-
def input(self) -> WebsocketClientInputTransport:
|
|
464
|
-
"""Get the input transport for receiving frames.
|
|
465
|
-
|
|
466
|
-
Returns:
|
|
467
|
-
The WebSocket client input transport instance.
|
|
468
|
-
"""
|
|
469
|
-
if not self._input:
|
|
470
|
-
self._input = WebsocketClientInputTransport(self, self._session, self._params)
|
|
471
|
-
return self._input
|
|
472
|
-
|
|
473
|
-
def output(self) -> WebsocketClientOutputTransport:
|
|
474
|
-
"""Get the output transport for sending frames.
|
|
475
|
-
|
|
476
|
-
Returns:
|
|
477
|
-
The WebSocket client output transport instance.
|
|
478
|
-
"""
|
|
479
|
-
if not self._output:
|
|
480
|
-
self._output = WebsocketClientOutputTransport(self, self._session, self._params)
|
|
481
|
-
return self._output
|
|
482
|
-
|
|
483
|
-
async def _on_connected(self, websocket):
|
|
484
|
-
"""Handle WebSocket connection established event."""
|
|
485
|
-
await self._call_event_handler("on_connected", websocket)
|
|
486
|
-
|
|
487
|
-
async def _on_disconnected(self, websocket):
|
|
488
|
-
"""Handle WebSocket connection closed event."""
|
|
489
|
-
await self._call_event_handler("on_disconnected", websocket)
|
|
490
|
-
|
|
491
|
-
async def _on_message(self, websocket, message):
|
|
492
|
-
"""Handle incoming WebSocket message."""
|
|
493
|
-
if self._input:
|
|
494
|
-
await self._input.on_message(websocket, message)
|
|
14
|
+
import warnings
|
|
15
|
+
|
|
16
|
+
from pipecat.transports.websocket.client import *
|
|
17
|
+
|
|
18
|
+
with warnings.catch_warnings():
|
|
19
|
+
warnings.simplefilter("always")
|
|
20
|
+
warnings.warn(
|
|
21
|
+
"Module `pipecat.transports.network.websocket_client` is deprecated, "
|
|
22
|
+
"use `pipecat.transports.websocket.client` instead.",
|
|
23
|
+
DeprecationWarning,
|
|
24
|
+
stacklevel=2,
|
|
25
|
+
)
|