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,12 @@
4
4
  # SPDX-License-Identifier: BSD 2-Clause License
5
5
  #
6
6
 
7
+ """FastAPI WebSocket transport implementation for Pipecat.
8
+
9
+ This module provides WebSocket-based transport for real-time audio/video streaming
10
+ using FastAPI and WebSocket connections. Supports binary and text serialization
11
+ with configurable session timeouts and WAV header generation.
12
+ """
7
13
 
8
14
  import asyncio
9
15
  import io
@@ -32,7 +38,6 @@ from pipecat.serializers.base_serializer import FrameSerializer, FrameSerializer
32
38
  from pipecat.transports.base_input import BaseInputTransport
33
39
  from pipecat.transports.base_output import BaseOutputTransport
34
40
  from pipecat.transports.base_transport import BaseTransport, TransportParams
35
- from pipecat.utils.asyncio.watchdog_async_iterator import WatchdogAsyncIterator
36
41
 
37
42
  try:
38
43
  from fastapi import WebSocket
@@ -46,24 +51,48 @@ except ModuleNotFoundError as e:
46
51
 
47
52
 
48
53
  class FastAPIWebsocketParams(TransportParams):
54
+ """Configuration parameters for FastAPI WebSocket transport.
55
+
56
+ Parameters:
57
+ add_wav_header: Whether to add WAV headers to audio frames.
58
+ serializer: Frame serializer for encoding/decoding messages.
59
+ session_timeout: Session timeout in seconds, None for no timeout.
60
+ """
61
+
49
62
  add_wav_header: bool = False
50
63
  serializer: Optional[FrameSerializer] = None
51
64
  session_timeout: Optional[int] = None
52
65
 
53
66
 
54
67
  class FastAPIWebsocketCallbacks(BaseModel):
68
+ """Callback functions for WebSocket events.
69
+
70
+ Parameters:
71
+ on_client_connected: Called when a client connects to the WebSocket.
72
+ on_client_disconnected: Called when a client disconnects from the WebSocket.
73
+ on_session_timeout: Called when a session timeout occurs.
74
+ """
75
+
55
76
  on_client_connected: Callable[[WebSocket], Awaitable[None]]
56
77
  on_client_disconnected: Callable[[WebSocket], Awaitable[None]]
57
78
  on_session_timeout: Callable[[WebSocket], Awaitable[None]]
58
79
 
59
80
 
60
81
  class FastAPIWebsocketClient:
61
- def __init__(
62
- self,
63
- websocket: WebSocket,
64
- is_binary: bool,
65
- callbacks: FastAPIWebsocketCallbacks,
66
- ):
82
+ """WebSocket client wrapper for handling connections and message passing.
83
+
84
+ Manages WebSocket state, message sending/receiving, and connection lifecycle
85
+ with support for both binary and text message types.
86
+ """
87
+
88
+ def __init__(self, websocket: WebSocket, is_binary: bool, callbacks: FastAPIWebsocketCallbacks):
89
+ """Initialize the WebSocket client.
90
+
91
+ Args:
92
+ websocket: The FastAPI WebSocket connection.
93
+ is_binary: Whether to use binary message format.
94
+ callbacks: Event callback functions.
95
+ """
67
96
  self._websocket = websocket
68
97
  self._closing = False
69
98
  self._is_binary = is_binary
@@ -72,14 +101,29 @@ class FastAPIWebsocketClient:
72
101
  self._conversation_id = None
73
102
 
74
103
  async def setup(self, _: StartFrame):
104
+ """Set up the WebSocket client.
105
+
106
+ Args:
107
+ _: The start frame (unused).
108
+ """
75
109
  self._leave_counter += 1
76
110
  if _.metadata and "call_id" in _.metadata:
77
111
  self._conversation_id = _.metadata["call_id"]
78
112
 
79
113
  def receive(self) -> typing.AsyncIterator[bytes | str]:
114
+ """Get an async iterator for receiving WebSocket messages.
115
+
116
+ Returns:
117
+ An async iterator yielding bytes or strings based on message type.
118
+ """
80
119
  return self._websocket.iter_bytes() if self._is_binary else self._websocket.iter_text()
81
120
 
82
121
  async def send(self, data: str | bytes):
122
+ """Send data through the WebSocket connection.
123
+
124
+ Args:
125
+ data: The data to send (string or bytes).
126
+ """
83
127
  try:
84
128
  if self._can_send():
85
129
  if self._is_binary:
@@ -110,6 +154,7 @@ class FastAPIWebsocketClient:
110
154
  await self.trigger_client_disconnected()
111
155
 
112
156
  async def disconnect(self):
157
+ """Disconnect the WebSocket client."""
113
158
  self._leave_counter -= 1
114
159
  if self._leave_counter > 0:
115
160
  return
@@ -123,27 +168,47 @@ class FastAPIWebsocketClient:
123
168
  await self.trigger_client_disconnected()
124
169
 
125
170
  async def trigger_client_disconnected(self):
171
+ """Trigger the client disconnected callback."""
126
172
  await self._callbacks.on_client_disconnected(self._websocket)
127
173
 
128
174
  async def trigger_client_connected(self):
175
+ """Trigger the client connected callback."""
129
176
  await self._callbacks.on_client_connected(self._websocket)
130
177
 
131
178
  async def trigger_client_timeout(self):
179
+ """Trigger the client timeout callback."""
132
180
  await self._callbacks.on_session_timeout(self._websocket)
133
181
 
134
182
  def _can_send(self):
183
+ """Check if data can be sent through the WebSocket."""
135
184
  return self.is_connected and not self.is_closing
136
185
 
137
186
  @property
138
187
  def is_connected(self) -> bool:
188
+ """Check if the WebSocket is currently connected.
189
+
190
+ Returns:
191
+ True if the WebSocket is in connected state.
192
+ """
139
193
  return self._websocket.client_state == WebSocketState.CONNECTED
140
194
 
141
195
  @property
142
196
  def is_closing(self) -> bool:
197
+ """Check if the WebSocket is currently closing.
198
+
199
+ Returns:
200
+ True if the WebSocket is in the process of closing.
201
+ """
143
202
  return self._closing
144
203
 
145
204
 
146
205
  class FastAPIWebsocketInputTransport(BaseInputTransport):
206
+ """Input transport for FastAPI WebSocket connections.
207
+
208
+ Handles incoming WebSocket messages, deserializes frames, and manages
209
+ connection monitoring with optional session timeouts.
210
+ """
211
+
147
212
  def __init__(
148
213
  self,
149
214
  transport: BaseTransport,
@@ -151,6 +216,14 @@ class FastAPIWebsocketInputTransport(BaseInputTransport):
151
216
  params: FastAPIWebsocketParams,
152
217
  **kwargs,
153
218
  ):
219
+ """Initialize the WebSocket input transport.
220
+
221
+ Args:
222
+ transport: The parent transport instance.
223
+ client: The WebSocket client wrapper.
224
+ params: Transport configuration parameters.
225
+ **kwargs: Additional arguments passed to parent class.
226
+ """
154
227
  super().__init__(params, **kwargs)
155
228
  self._transport = transport
156
229
  self._client = client
@@ -162,6 +235,11 @@ class FastAPIWebsocketInputTransport(BaseInputTransport):
162
235
  self._initialized = False
163
236
 
164
237
  async def start(self, frame: StartFrame):
238
+ """Start the input transport and begin message processing.
239
+
240
+ Args:
241
+ frame: The start frame containing initialization parameters.
242
+ """
165
243
  await super().start(frame)
166
244
 
167
245
  if self._initialized:
@@ -180,6 +258,7 @@ class FastAPIWebsocketInputTransport(BaseInputTransport):
180
258
  await self.set_transport_ready(frame)
181
259
 
182
260
  async def _stop_tasks(self):
261
+ """Stop all running tasks."""
183
262
  if self._monitor_websocket_task:
184
263
  await self.cancel_task(self._monitor_websocket_task)
185
264
  self._monitor_websocket_task = None
@@ -188,24 +267,34 @@ class FastAPIWebsocketInputTransport(BaseInputTransport):
188
267
  self._receive_task = None
189
268
 
190
269
  async def stop(self, frame: EndFrame):
270
+ """Stop the input transport and cleanup resources.
271
+
272
+ Args:
273
+ frame: The end frame signaling transport shutdown.
274
+ """
191
275
  await super().stop(frame)
192
276
  await self._stop_tasks()
193
277
  await self._client.disconnect()
194
278
 
195
279
  async def cancel(self, frame: CancelFrame):
280
+ """Cancel the input transport and stop all processing.
281
+
282
+ Args:
283
+ frame: The cancel frame signaling immediate cancellation.
284
+ """
196
285
  await super().cancel(frame)
197
286
  await self._stop_tasks()
198
287
  await self._client.disconnect()
199
288
 
200
289
  async def cleanup(self):
290
+ """Clean up transport resources."""
201
291
  await super().cleanup()
202
292
  await self._transport.cleanup()
203
293
 
204
294
  async def _receive_messages(self):
295
+ """Main message receiving loop for WebSocket messages."""
205
296
  try:
206
- async for message in WatchdogAsyncIterator(
207
- self._client.receive(), manager=self.task_manager
208
- ):
297
+ async for message in self._client.receive():
209
298
  if not self._params.serializer:
210
299
  continue
211
300
 
@@ -230,6 +319,12 @@ class FastAPIWebsocketInputTransport(BaseInputTransport):
230
319
 
231
320
 
232
321
  class FastAPIWebsocketOutputTransport(BaseOutputTransport):
322
+ """Output transport for FastAPI WebSocket connections.
323
+
324
+ Handles outgoing frame serialization, audio streaming with timing simulation,
325
+ and WebSocket message transmission with optional WAV header generation.
326
+ """
327
+
233
328
  def __init__(
234
329
  self,
235
330
  transport: BaseTransport,
@@ -237,6 +332,14 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport):
237
332
  params: FastAPIWebsocketParams,
238
333
  **kwargs,
239
334
  ):
335
+ """Initialize the WebSocket output transport.
336
+
337
+ Args:
338
+ transport: The parent transport instance.
339
+ client: The WebSocket client wrapper.
340
+ params: Transport configuration parameters.
341
+ **kwargs: Additional arguments passed to parent class.
342
+ """
240
343
  super().__init__(params, **kwargs)
241
344
 
242
345
  self._transport = transport
@@ -255,6 +358,11 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport):
255
358
  self._initialized = False
256
359
 
257
360
  async def start(self, frame: StartFrame):
361
+ """Start the output transport and initialize timing.
362
+
363
+ Args:
364
+ frame: The start frame containing initialization parameters.
365
+ """
258
366
  await super().start(frame)
259
367
 
260
368
  if self._initialized:
@@ -269,20 +377,37 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport):
269
377
  await self.set_transport_ready(frame)
270
378
 
271
379
  async def stop(self, frame: EndFrame):
380
+ """Stop the output transport and cleanup resources.
381
+
382
+ Args:
383
+ frame: The end frame signaling transport shutdown.
384
+ """
272
385
  await super().stop(frame)
273
386
  await self._write_frame(frame)
274
387
  await self._client.disconnect()
275
388
 
276
389
  async def cancel(self, frame: CancelFrame):
390
+ """Cancel the output transport and stop all processing.
391
+
392
+ Args:
393
+ frame: The cancel frame signaling immediate cancellation.
394
+ """
277
395
  await super().cancel(frame)
278
396
  await self._write_frame(frame)
279
397
  await self._client.disconnect()
280
398
 
281
399
  async def cleanup(self):
400
+ """Clean up transport resources."""
282
401
  await super().cleanup()
283
402
  await self._transport.cleanup()
284
403
 
285
404
  async def process_frame(self, frame: Frame, direction: FrameDirection):
405
+ """Process outgoing frames with special handling for interruptions.
406
+
407
+ Args:
408
+ frame: The frame to process.
409
+ direction: The direction of frame flow in the pipeline.
410
+ """
286
411
  await super().process_frame(frame, direction)
287
412
 
288
413
  if isinstance(frame, StartInterruptionFrame):
@@ -290,15 +415,20 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport):
290
415
  self._next_send_time = 0
291
416
 
292
417
  async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame):
418
+ """Send a transport message frame.
419
+
420
+ Args:
421
+ frame: The transport message frame to send.
422
+ """
293
423
  await self._write_frame(frame)
294
424
 
295
425
  async def write_audio_frame(self, frame: OutputAudioRawFrame):
296
- if self._client.is_closing:
297
- return
426
+ """Write an audio frame to the WebSocket with timing simulation.
298
427
 
299
- if not self._client.is_connected:
300
- # Simulate audio playback with a sleep.
301
- await self._write_audio_sleep()
428
+ Args:
429
+ frame: The output audio frame to write.
430
+ """
431
+ if self._client.is_closing or not self._client.is_connected:
302
432
  return
303
433
 
304
434
  frame = OutputAudioRawFrame(
@@ -327,6 +457,7 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport):
327
457
  await self._write_audio_sleep()
328
458
 
329
459
  async def _write_frame(self, frame: Frame):
460
+ """Serialize and send a frame through the WebSocket."""
330
461
  if not self._params.serializer:
331
462
  return
332
463
 
@@ -338,6 +469,7 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport):
338
469
  logger.error(f"{self} exception sending data: {e.__class__.__name__} ({e})")
339
470
 
340
471
  async def _write_audio_sleep(self):
472
+ """Simulate audio playback timing with appropriate delays."""
341
473
  # Simulate a clock.
342
474
  current_time = time.monotonic()
343
475
  sleep_duration = max(0, self._next_send_time - current_time)
@@ -349,6 +481,12 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport):
349
481
 
350
482
 
351
483
  class FastAPIWebsocketTransport(BaseTransport):
484
+ """FastAPI WebSocket transport for real-time audio/video streaming.
485
+
486
+ Provides bidirectional WebSocket communication with frame serialization,
487
+ session management, and event handling for client connections and timeouts.
488
+ """
489
+
352
490
  def __init__(
353
491
  self,
354
492
  websocket: WebSocket,
@@ -356,6 +494,14 @@ class FastAPIWebsocketTransport(BaseTransport):
356
494
  input_name: Optional[str] = None,
357
495
  output_name: Optional[str] = None,
358
496
  ):
497
+ """Initialize the FastAPI WebSocket transport.
498
+
499
+ Args:
500
+ websocket: The FastAPI WebSocket connection.
501
+ params: Transport configuration parameters.
502
+ input_name: Optional name for the input processor.
503
+ output_name: Optional name for the output processor.
504
+ """
359
505
  super().__init__(input_name=input_name, output_name=output_name)
360
506
 
361
507
  self._params = params
@@ -385,16 +531,29 @@ class FastAPIWebsocketTransport(BaseTransport):
385
531
  self._register_event_handler("on_session_timeout")
386
532
 
387
533
  def input(self) -> FastAPIWebsocketInputTransport:
534
+ """Get the input transport processor.
535
+
536
+ Returns:
537
+ The WebSocket input transport instance.
538
+ """
388
539
  return self._input
389
540
 
390
541
  def output(self) -> FastAPIWebsocketOutputTransport:
542
+ """Get the output transport processor.
543
+
544
+ Returns:
545
+ The WebSocket output transport instance.
546
+ """
391
547
  return self._output
392
548
 
393
549
  async def _on_client_connected(self, websocket):
550
+ """Handle client connected event."""
394
551
  await self._call_event_handler("on_client_connected", websocket)
395
552
 
396
553
  async def _on_client_disconnected(self, websocket):
554
+ """Handle client disconnected event."""
397
555
  await self._call_event_handler("on_client_disconnected", websocket)
398
556
 
399
557
  async def _on_session_timeout(self, websocket):
558
+ """Handle session timeout event."""
400
559
  await self._call_event_handler("on_session_timeout", websocket)