dv-pipecat-ai 0.0.74.dev770__py3-none-any.whl → 0.0.82.dev776__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 (244) hide show
  1. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/METADATA +137 -93
  2. dv_pipecat_ai-0.0.82.dev776.dist-info/RECORD +340 -0
  3. pipecat/__init__.py +17 -0
  4. pipecat/adapters/base_llm_adapter.py +36 -1
  5. pipecat/adapters/schemas/direct_function.py +296 -0
  6. pipecat/adapters/schemas/function_schema.py +15 -6
  7. pipecat/adapters/schemas/tools_schema.py +55 -7
  8. pipecat/adapters/services/anthropic_adapter.py +22 -3
  9. pipecat/adapters/services/aws_nova_sonic_adapter.py +23 -3
  10. pipecat/adapters/services/bedrock_adapter.py +22 -3
  11. pipecat/adapters/services/gemini_adapter.py +16 -3
  12. pipecat/adapters/services/open_ai_adapter.py +17 -2
  13. pipecat/adapters/services/open_ai_realtime_adapter.py +23 -3
  14. pipecat/audio/filters/base_audio_filter.py +30 -6
  15. pipecat/audio/filters/koala_filter.py +37 -2
  16. pipecat/audio/filters/krisp_filter.py +59 -6
  17. pipecat/audio/filters/noisereduce_filter.py +37 -0
  18. pipecat/audio/interruptions/base_interruption_strategy.py +25 -5
  19. pipecat/audio/interruptions/min_words_interruption_strategy.py +21 -4
  20. pipecat/audio/mixers/base_audio_mixer.py +30 -7
  21. pipecat/audio/mixers/soundfile_mixer.py +53 -6
  22. pipecat/audio/resamplers/base_audio_resampler.py +17 -9
  23. pipecat/audio/resamplers/resampy_resampler.py +26 -1
  24. pipecat/audio/resamplers/soxr_resampler.py +32 -1
  25. pipecat/audio/resamplers/soxr_stream_resampler.py +101 -0
  26. pipecat/audio/utils.py +194 -1
  27. pipecat/audio/vad/silero.py +60 -3
  28. pipecat/audio/vad/vad_analyzer.py +114 -30
  29. pipecat/clocks/base_clock.py +19 -0
  30. pipecat/clocks/system_clock.py +25 -0
  31. pipecat/extensions/voicemail/__init__.py +0 -0
  32. pipecat/extensions/voicemail/voicemail_detector.py +707 -0
  33. pipecat/frames/frames.py +590 -156
  34. pipecat/metrics/metrics.py +64 -1
  35. pipecat/observers/base_observer.py +58 -19
  36. pipecat/observers/loggers/debug_log_observer.py +56 -64
  37. pipecat/observers/loggers/llm_log_observer.py +8 -1
  38. pipecat/observers/loggers/transcription_log_observer.py +19 -7
  39. pipecat/observers/loggers/user_bot_latency_log_observer.py +32 -5
  40. pipecat/observers/turn_tracking_observer.py +26 -1
  41. pipecat/pipeline/base_pipeline.py +5 -7
  42. pipecat/pipeline/base_task.py +52 -9
  43. pipecat/pipeline/parallel_pipeline.py +121 -177
  44. pipecat/pipeline/pipeline.py +129 -20
  45. pipecat/pipeline/runner.py +50 -1
  46. pipecat/pipeline/sync_parallel_pipeline.py +132 -32
  47. pipecat/pipeline/task.py +263 -280
  48. pipecat/pipeline/task_observer.py +85 -34
  49. pipecat/pipeline/to_be_updated/merge_pipeline.py +32 -2
  50. pipecat/processors/aggregators/dtmf_aggregator.py +29 -22
  51. pipecat/processors/aggregators/gated.py +25 -24
  52. pipecat/processors/aggregators/gated_openai_llm_context.py +22 -2
  53. pipecat/processors/aggregators/llm_response.py +398 -89
  54. pipecat/processors/aggregators/openai_llm_context.py +161 -13
  55. pipecat/processors/aggregators/sentence.py +25 -14
  56. pipecat/processors/aggregators/user_response.py +28 -3
  57. pipecat/processors/aggregators/vision_image_frame.py +24 -14
  58. pipecat/processors/async_generator.py +28 -0
  59. pipecat/processors/audio/audio_buffer_processor.py +78 -37
  60. pipecat/processors/consumer_processor.py +25 -6
  61. pipecat/processors/filters/frame_filter.py +23 -0
  62. pipecat/processors/filters/function_filter.py +30 -0
  63. pipecat/processors/filters/identity_filter.py +17 -2
  64. pipecat/processors/filters/null_filter.py +24 -1
  65. pipecat/processors/filters/stt_mute_filter.py +56 -21
  66. pipecat/processors/filters/wake_check_filter.py +46 -3
  67. pipecat/processors/filters/wake_notifier_filter.py +21 -3
  68. pipecat/processors/frame_processor.py +488 -131
  69. pipecat/processors/frameworks/langchain.py +38 -3
  70. pipecat/processors/frameworks/rtvi.py +719 -34
  71. pipecat/processors/gstreamer/pipeline_source.py +41 -0
  72. pipecat/processors/idle_frame_processor.py +26 -3
  73. pipecat/processors/logger.py +23 -0
  74. pipecat/processors/metrics/frame_processor_metrics.py +77 -4
  75. pipecat/processors/metrics/sentry.py +42 -4
  76. pipecat/processors/producer_processor.py +34 -14
  77. pipecat/processors/text_transformer.py +22 -10
  78. pipecat/processors/transcript_processor.py +48 -29
  79. pipecat/processors/user_idle_processor.py +31 -21
  80. pipecat/runner/__init__.py +1 -0
  81. pipecat/runner/daily.py +132 -0
  82. pipecat/runner/livekit.py +148 -0
  83. pipecat/runner/run.py +543 -0
  84. pipecat/runner/types.py +67 -0
  85. pipecat/runner/utils.py +515 -0
  86. pipecat/serializers/base_serializer.py +42 -0
  87. pipecat/serializers/exotel.py +17 -6
  88. pipecat/serializers/genesys.py +95 -0
  89. pipecat/serializers/livekit.py +33 -0
  90. pipecat/serializers/plivo.py +16 -15
  91. pipecat/serializers/protobuf.py +37 -1
  92. pipecat/serializers/telnyx.py +18 -17
  93. pipecat/serializers/twilio.py +32 -16
  94. pipecat/services/ai_service.py +5 -3
  95. pipecat/services/anthropic/llm.py +113 -43
  96. pipecat/services/assemblyai/models.py +63 -5
  97. pipecat/services/assemblyai/stt.py +64 -11
  98. pipecat/services/asyncai/__init__.py +0 -0
  99. pipecat/services/asyncai/tts.py +501 -0
  100. pipecat/services/aws/llm.py +185 -111
  101. pipecat/services/aws/stt.py +217 -23
  102. pipecat/services/aws/tts.py +118 -52
  103. pipecat/services/aws/utils.py +101 -5
  104. pipecat/services/aws_nova_sonic/aws.py +82 -64
  105. pipecat/services/aws_nova_sonic/context.py +15 -6
  106. pipecat/services/azure/common.py +10 -2
  107. pipecat/services/azure/image.py +32 -0
  108. pipecat/services/azure/llm.py +9 -7
  109. pipecat/services/azure/stt.py +65 -2
  110. pipecat/services/azure/tts.py +154 -23
  111. pipecat/services/cartesia/stt.py +125 -8
  112. pipecat/services/cartesia/tts.py +102 -38
  113. pipecat/services/cerebras/llm.py +15 -23
  114. pipecat/services/deepgram/stt.py +19 -11
  115. pipecat/services/deepgram/tts.py +36 -0
  116. pipecat/services/deepseek/llm.py +14 -23
  117. pipecat/services/elevenlabs/tts.py +330 -64
  118. pipecat/services/fal/image.py +43 -0
  119. pipecat/services/fal/stt.py +48 -10
  120. pipecat/services/fireworks/llm.py +14 -21
  121. pipecat/services/fish/tts.py +109 -9
  122. pipecat/services/gemini_multimodal_live/__init__.py +1 -0
  123. pipecat/services/gemini_multimodal_live/events.py +83 -2
  124. pipecat/services/gemini_multimodal_live/file_api.py +189 -0
  125. pipecat/services/gemini_multimodal_live/gemini.py +218 -21
  126. pipecat/services/gladia/config.py +17 -10
  127. pipecat/services/gladia/stt.py +82 -36
  128. pipecat/services/google/frames.py +40 -0
  129. pipecat/services/google/google.py +2 -0
  130. pipecat/services/google/image.py +39 -2
  131. pipecat/services/google/llm.py +176 -58
  132. pipecat/services/google/llm_openai.py +26 -4
  133. pipecat/services/google/llm_vertex.py +37 -15
  134. pipecat/services/google/rtvi.py +41 -0
  135. pipecat/services/google/stt.py +65 -17
  136. pipecat/services/google/test-google-chirp.py +45 -0
  137. pipecat/services/google/tts.py +390 -19
  138. pipecat/services/grok/llm.py +8 -6
  139. pipecat/services/groq/llm.py +8 -6
  140. pipecat/services/groq/stt.py +13 -9
  141. pipecat/services/groq/tts.py +40 -0
  142. pipecat/services/hamsa/__init__.py +9 -0
  143. pipecat/services/hamsa/stt.py +241 -0
  144. pipecat/services/heygen/__init__.py +5 -0
  145. pipecat/services/heygen/api.py +281 -0
  146. pipecat/services/heygen/client.py +620 -0
  147. pipecat/services/heygen/video.py +338 -0
  148. pipecat/services/image_service.py +5 -3
  149. pipecat/services/inworld/__init__.py +1 -0
  150. pipecat/services/inworld/tts.py +592 -0
  151. pipecat/services/llm_service.py +127 -45
  152. pipecat/services/lmnt/tts.py +80 -7
  153. pipecat/services/mcp_service.py +85 -44
  154. pipecat/services/mem0/memory.py +42 -13
  155. pipecat/services/minimax/tts.py +74 -15
  156. pipecat/services/mistral/__init__.py +0 -0
  157. pipecat/services/mistral/llm.py +185 -0
  158. pipecat/services/moondream/vision.py +55 -10
  159. pipecat/services/neuphonic/tts.py +275 -48
  160. pipecat/services/nim/llm.py +8 -6
  161. pipecat/services/ollama/llm.py +27 -7
  162. pipecat/services/openai/base_llm.py +54 -16
  163. pipecat/services/openai/image.py +30 -0
  164. pipecat/services/openai/llm.py +7 -5
  165. pipecat/services/openai/stt.py +13 -9
  166. pipecat/services/openai/tts.py +42 -10
  167. pipecat/services/openai_realtime_beta/azure.py +11 -9
  168. pipecat/services/openai_realtime_beta/context.py +7 -5
  169. pipecat/services/openai_realtime_beta/events.py +10 -7
  170. pipecat/services/openai_realtime_beta/openai.py +37 -18
  171. pipecat/services/openpipe/llm.py +30 -24
  172. pipecat/services/openrouter/llm.py +9 -7
  173. pipecat/services/perplexity/llm.py +15 -19
  174. pipecat/services/piper/tts.py +26 -12
  175. pipecat/services/playht/tts.py +227 -65
  176. pipecat/services/qwen/llm.py +8 -6
  177. pipecat/services/rime/tts.py +128 -17
  178. pipecat/services/riva/stt.py +160 -22
  179. pipecat/services/riva/tts.py +67 -2
  180. pipecat/services/sambanova/llm.py +19 -17
  181. pipecat/services/sambanova/stt.py +14 -8
  182. pipecat/services/sarvam/tts.py +60 -13
  183. pipecat/services/simli/video.py +82 -21
  184. pipecat/services/soniox/__init__.py +0 -0
  185. pipecat/services/soniox/stt.py +398 -0
  186. pipecat/services/speechmatics/stt.py +29 -17
  187. pipecat/services/stt_service.py +47 -11
  188. pipecat/services/tavus/video.py +94 -25
  189. pipecat/services/together/llm.py +8 -6
  190. pipecat/services/tts_service.py +77 -53
  191. pipecat/services/ultravox/stt.py +46 -43
  192. pipecat/services/vision_service.py +5 -3
  193. pipecat/services/websocket_service.py +12 -11
  194. pipecat/services/whisper/base_stt.py +58 -12
  195. pipecat/services/whisper/stt.py +69 -58
  196. pipecat/services/xtts/tts.py +59 -2
  197. pipecat/sync/base_notifier.py +19 -0
  198. pipecat/sync/event_notifier.py +24 -0
  199. pipecat/tests/utils.py +73 -5
  200. pipecat/transcriptions/language.py +24 -0
  201. pipecat/transports/base_input.py +112 -8
  202. pipecat/transports/base_output.py +235 -13
  203. pipecat/transports/base_transport.py +119 -0
  204. pipecat/transports/local/audio.py +76 -0
  205. pipecat/transports/local/tk.py +84 -0
  206. pipecat/transports/network/fastapi_websocket.py +174 -15
  207. pipecat/transports/network/small_webrtc.py +383 -39
  208. pipecat/transports/network/webrtc_connection.py +214 -8
  209. pipecat/transports/network/websocket_client.py +171 -1
  210. pipecat/transports/network/websocket_server.py +147 -9
  211. pipecat/transports/services/daily.py +792 -70
  212. pipecat/transports/services/helpers/daily_rest.py +122 -129
  213. pipecat/transports/services/livekit.py +339 -4
  214. pipecat/transports/services/tavus.py +273 -38
  215. pipecat/utils/asyncio/task_manager.py +92 -186
  216. pipecat/utils/base_object.py +83 -1
  217. pipecat/utils/network.py +2 -0
  218. pipecat/utils/string.py +114 -58
  219. pipecat/utils/text/base_text_aggregator.py +44 -13
  220. pipecat/utils/text/base_text_filter.py +46 -0
  221. pipecat/utils/text/markdown_text_filter.py +70 -14
  222. pipecat/utils/text/pattern_pair_aggregator.py +18 -14
  223. pipecat/utils/text/simple_text_aggregator.py +43 -2
  224. pipecat/utils/text/skip_tags_aggregator.py +21 -13
  225. pipecat/utils/time.py +36 -0
  226. pipecat/utils/tracing/class_decorators.py +32 -7
  227. pipecat/utils/tracing/conversation_context_provider.py +12 -2
  228. pipecat/utils/tracing/service_attributes.py +80 -64
  229. pipecat/utils/tracing/service_decorators.py +48 -21
  230. pipecat/utils/tracing/setup.py +13 -7
  231. pipecat/utils/tracing/turn_context_provider.py +12 -2
  232. pipecat/utils/tracing/turn_trace_observer.py +27 -0
  233. pipecat/utils/utils.py +14 -14
  234. dv_pipecat_ai-0.0.74.dev770.dist-info/RECORD +0 -319
  235. pipecat/examples/daily_runner.py +0 -64
  236. pipecat/examples/run.py +0 -265
  237. pipecat/utils/asyncio/watchdog_async_iterator.py +0 -72
  238. pipecat/utils/asyncio/watchdog_event.py +0 -42
  239. pipecat/utils/asyncio/watchdog_priority_queue.py +0 -48
  240. pipecat/utils/asyncio/watchdog_queue.py +0 -48
  241. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/WHEEL +0 -0
  242. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/licenses/LICENSE +0 -0
  243. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/top_level.txt +0 -0
  244. /pipecat/{examples → extensions}/__init__.py +0 -0
@@ -4,6 +4,13 @@
4
4
  # SPDX-License-Identifier: BSD 2-Clause License
5
5
  #
6
6
 
7
+ """WebSocket client transport implementation for Pipecat.
8
+
9
+ This module provides a WebSocket client transport that enables bidirectional
10
+ communication over WebSocket connections, with support for audio streaming,
11
+ frame serialization, and connection management.
12
+ """
13
+
7
14
  import asyncio
8
15
  import io
9
16
  import time
@@ -13,6 +20,7 @@ from typing import Awaitable, Callable, Optional
13
20
  import websockets
14
21
  from loguru import logger
15
22
  from pydantic.main import BaseModel
23
+ from websockets.asyncio.client import connect as websocket_connect
16
24
 
17
25
  from pipecat.frames.frames import (
18
26
  CancelFrame,
@@ -34,17 +42,38 @@ from pipecat.utils.asyncio.task_manager import BaseTaskManager
34
42
 
35
43
 
36
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
+
37
52
  add_wav_header: bool = True
38
53
  serializer: Optional[FrameSerializer] = None
39
54
 
40
55
 
41
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
+
42
65
  on_connected: Callable[[websockets.WebSocketClientProtocol], Awaitable[None]]
43
66
  on_disconnected: Callable[[websockets.WebSocketClientProtocol], Awaitable[None]]
44
67
  on_message: Callable[[websockets.WebSocketClientProtocol, websockets.Data], Awaitable[None]]
45
68
 
46
69
 
47
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
+
48
77
  def __init__(
49
78
  self,
50
79
  uri: str,
@@ -52,6 +81,14 @@ class WebsocketClientSession:
52
81
  callbacks: WebsocketClientCallbacks,
53
82
  transport_name: str,
54
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
+ """
55
92
  self._uri = uri
56
93
  self._params = params
57
94
  self._callbacks = callbacks
@@ -63,6 +100,14 @@ class WebsocketClientSession:
63
100
 
64
101
  @property
65
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
+ """
66
111
  if not self._task_manager:
67
112
  raise Exception(
68
113
  f"{self._transport_name}::WebsocketClientSession: TaskManager not initialized (pipeline not started?)"
@@ -70,16 +115,22 @@ class WebsocketClientSession:
70
115
  return self._task_manager
71
116
 
72
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
+ """
73
123
  self._leave_counter += 1
74
124
  if not self._task_manager:
75
125
  self._task_manager = task_manager
76
126
 
77
127
  async def connect(self):
128
+ """Connect to the WebSocket server."""
78
129
  if self._websocket:
79
130
  return
80
131
 
81
132
  try:
82
- self._websocket = await websockets.connect(uri=self._uri, open_timeout=10)
133
+ self._websocket = await websocket_connect(uri=self._uri, open_timeout=10)
83
134
  self._client_task = self.task_manager.create_task(
84
135
  self._client_task_handler(),
85
136
  f"{self._transport_name}::WebsocketClientSession::_client_task_handler",
@@ -89,6 +140,7 @@ class WebsocketClientSession:
89
140
  logger.error(f"Timeout connecting to {self._uri}")
90
141
 
91
142
  async def disconnect(self):
143
+ """Disconnect from the WebSocket server."""
92
144
  self._leave_counter -= 1
93
145
  if not self._websocket or self._leave_counter > 0:
94
146
  return
@@ -99,6 +151,11 @@ class WebsocketClientSession:
99
151
  self._websocket = None
100
152
 
101
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
+ """
102
159
  try:
103
160
  if self._websocket:
104
161
  await self._websocket.send(message)
@@ -106,6 +163,7 @@ class WebsocketClientSession:
106
163
  logger.error(f"{self} exception sending data: {e.__class__.__name__} ({e})")
107
164
 
108
165
  async def _client_task_handler(self):
166
+ """Handle incoming messages from the WebSocket connection."""
109
167
  try:
110
168
  # Handle incoming messages
111
169
  async for message in self._websocket:
@@ -116,16 +174,30 @@ class WebsocketClientSession:
116
174
  await self._callbacks.on_disconnected(self._websocket)
117
175
 
118
176
  def __str__(self):
177
+ """String representation of the WebSocket client session."""
119
178
  return f"{self._transport_name}::WebsocketClientSession"
120
179
 
121
180
 
122
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
+
123
188
  def __init__(
124
189
  self,
125
190
  transport: BaseTransport,
126
191
  session: WebsocketClientSession,
127
192
  params: WebsocketClientParams,
128
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
+ """
129
201
  super().__init__(params)
130
202
 
131
203
  self._transport = transport
@@ -136,10 +208,20 @@ class WebsocketClientInputTransport(BaseInputTransport):
136
208
  self._initialized = False
137
209
 
138
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
+ """
139
216
  await super().setup(setup)
140
217
  await self._session.setup(setup.task_manager)
141
218
 
142
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
+ """
143
225
  await super().start(frame)
144
226
 
145
227
  if self._initialized:
@@ -153,18 +235,35 @@ class WebsocketClientInputTransport(BaseInputTransport):
153
235
  await self.set_transport_ready(frame)
154
236
 
155
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
+ """
156
243
  await super().stop(frame)
157
244
  await self._session.disconnect()
158
245
 
159
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
+ """
160
252
  await super().cancel(frame)
161
253
  await self._session.disconnect()
162
254
 
163
255
  async def cleanup(self):
256
+ """Clean up the input transport resources."""
164
257
  await super().cleanup()
165
258
  await self._transport.cleanup()
166
259
 
167
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
+ """
168
267
  if not self._params.serializer:
169
268
  return
170
269
  frame = await self._params.serializer.deserialize(message)
@@ -177,12 +276,25 @@ class WebsocketClientInputTransport(BaseInputTransport):
177
276
 
178
277
 
179
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
+
180
285
  def __init__(
181
286
  self,
182
287
  transport: BaseTransport,
183
288
  session: WebsocketClientSession,
184
289
  params: WebsocketClientParams,
185
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
+ """
186
298
  super().__init__(params)
187
299
 
188
300
  self._transport = transport
@@ -201,10 +313,20 @@ class WebsocketClientOutputTransport(BaseOutputTransport):
201
313
  self._initialized = False
202
314
 
203
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
+ """
204
321
  await super().setup(setup)
205
322
  await self._session.setup(setup.task_manager)
206
323
 
207
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
+ """
208
330
  await super().start(frame)
209
331
 
210
332
  if self._initialized:
@@ -219,21 +341,42 @@ class WebsocketClientOutputTransport(BaseOutputTransport):
219
341
  await self.set_transport_ready(frame)
220
342
 
221
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
+ """
222
349
  await super().stop(frame)
223
350
  await self._session.disconnect()
224
351
 
225
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
+ """
226
358
  await super().cancel(frame)
227
359
  await self._session.disconnect()
228
360
 
229
361
  async def cleanup(self):
362
+ """Clean up the output transport resources."""
230
363
  await super().cleanup()
231
364
  await self._transport.cleanup()
232
365
 
233
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
+ """
234
372
  await self._write_frame(frame)
235
373
 
236
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
+ """
237
380
  frame = OutputAudioRawFrame(
238
381
  audio=frame.audio,
239
382
  sample_rate=self.sample_rate,
@@ -260,6 +403,7 @@ class WebsocketClientOutputTransport(BaseOutputTransport):
260
403
  await self._write_audio_sleep()
261
404
 
262
405
  async def _write_frame(self, frame: Frame):
406
+ """Write a frame to the WebSocket after serialization."""
263
407
  if not self._params.serializer:
264
408
  return
265
409
  payload = await self._params.serializer.serialize(frame)
@@ -267,6 +411,7 @@ class WebsocketClientOutputTransport(BaseOutputTransport):
267
411
  await self._session.send(payload)
268
412
 
269
413
  async def _write_audio_sleep(self):
414
+ """Simulate audio playback timing with sleep delays."""
270
415
  # Simulate a clock.
271
416
  current_time = time.monotonic()
272
417
  sleep_duration = max(0, self._next_send_time - current_time)
@@ -278,11 +423,23 @@ class WebsocketClientOutputTransport(BaseOutputTransport):
278
423
 
279
424
 
280
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
+
281
432
  def __init__(
282
433
  self,
283
434
  uri: str,
284
435
  params: Optional[WebsocketClientParams] = None,
285
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
+ """
286
443
  super().__init__()
287
444
 
288
445
  self._params = params or WebsocketClientParams()
@@ -304,21 +461,34 @@ class WebsocketClientTransport(BaseTransport):
304
461
  self._register_event_handler("on_disconnected")
305
462
 
306
463
  def input(self) -> WebsocketClientInputTransport:
464
+ """Get the input transport for receiving frames.
465
+
466
+ Returns:
467
+ The WebSocket client input transport instance.
468
+ """
307
469
  if not self._input:
308
470
  self._input = WebsocketClientInputTransport(self, self._session, self._params)
309
471
  return self._input
310
472
 
311
473
  def output(self) -> WebsocketClientOutputTransport:
474
+ """Get the output transport for sending frames.
475
+
476
+ Returns:
477
+ The WebSocket client output transport instance.
478
+ """
312
479
  if not self._output:
313
480
  self._output = WebsocketClientOutputTransport(self, self._session, self._params)
314
481
  return self._output
315
482
 
316
483
  async def _on_connected(self, websocket):
484
+ """Handle WebSocket connection established event."""
317
485
  await self._call_event_handler("on_connected", websocket)
318
486
 
319
487
  async def _on_disconnected(self, websocket):
488
+ """Handle WebSocket connection closed event."""
320
489
  await self._call_event_handler("on_disconnected", websocket)
321
490
 
322
491
  async def _on_message(self, websocket, message):
492
+ """Handle incoming WebSocket message."""
323
493
  if self._input:
324
494
  await self._input.on_message(websocket, message)