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.
- {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/METADATA +137 -93
- dv_pipecat_ai-0.0.82.dev776.dist-info/RECORD +340 -0
- pipecat/__init__.py +17 -0
- pipecat/adapters/base_llm_adapter.py +36 -1
- pipecat/adapters/schemas/direct_function.py +296 -0
- pipecat/adapters/schemas/function_schema.py +15 -6
- pipecat/adapters/schemas/tools_schema.py +55 -7
- pipecat/adapters/services/anthropic_adapter.py +22 -3
- pipecat/adapters/services/aws_nova_sonic_adapter.py +23 -3
- pipecat/adapters/services/bedrock_adapter.py +22 -3
- pipecat/adapters/services/gemini_adapter.py +16 -3
- pipecat/adapters/services/open_ai_adapter.py +17 -2
- pipecat/adapters/services/open_ai_realtime_adapter.py +23 -3
- pipecat/audio/filters/base_audio_filter.py +30 -6
- pipecat/audio/filters/koala_filter.py +37 -2
- pipecat/audio/filters/krisp_filter.py +59 -6
- pipecat/audio/filters/noisereduce_filter.py +37 -0
- pipecat/audio/interruptions/base_interruption_strategy.py +25 -5
- pipecat/audio/interruptions/min_words_interruption_strategy.py +21 -4
- pipecat/audio/mixers/base_audio_mixer.py +30 -7
- pipecat/audio/mixers/soundfile_mixer.py +53 -6
- pipecat/audio/resamplers/base_audio_resampler.py +17 -9
- pipecat/audio/resamplers/resampy_resampler.py +26 -1
- pipecat/audio/resamplers/soxr_resampler.py +32 -1
- pipecat/audio/resamplers/soxr_stream_resampler.py +101 -0
- pipecat/audio/utils.py +194 -1
- pipecat/audio/vad/silero.py +60 -3
- pipecat/audio/vad/vad_analyzer.py +114 -30
- pipecat/clocks/base_clock.py +19 -0
- pipecat/clocks/system_clock.py +25 -0
- pipecat/extensions/voicemail/__init__.py +0 -0
- pipecat/extensions/voicemail/voicemail_detector.py +707 -0
- pipecat/frames/frames.py +590 -156
- pipecat/metrics/metrics.py +64 -1
- pipecat/observers/base_observer.py +58 -19
- pipecat/observers/loggers/debug_log_observer.py +56 -64
- pipecat/observers/loggers/llm_log_observer.py +8 -1
- pipecat/observers/loggers/transcription_log_observer.py +19 -7
- pipecat/observers/loggers/user_bot_latency_log_observer.py +32 -5
- pipecat/observers/turn_tracking_observer.py +26 -1
- pipecat/pipeline/base_pipeline.py +5 -7
- pipecat/pipeline/base_task.py +52 -9
- pipecat/pipeline/parallel_pipeline.py +121 -177
- pipecat/pipeline/pipeline.py +129 -20
- pipecat/pipeline/runner.py +50 -1
- pipecat/pipeline/sync_parallel_pipeline.py +132 -32
- pipecat/pipeline/task.py +263 -280
- pipecat/pipeline/task_observer.py +85 -34
- pipecat/pipeline/to_be_updated/merge_pipeline.py +32 -2
- pipecat/processors/aggregators/dtmf_aggregator.py +29 -22
- pipecat/processors/aggregators/gated.py +25 -24
- pipecat/processors/aggregators/gated_openai_llm_context.py +22 -2
- pipecat/processors/aggregators/llm_response.py +398 -89
- pipecat/processors/aggregators/openai_llm_context.py +161 -13
- pipecat/processors/aggregators/sentence.py +25 -14
- pipecat/processors/aggregators/user_response.py +28 -3
- pipecat/processors/aggregators/vision_image_frame.py +24 -14
- pipecat/processors/async_generator.py +28 -0
- pipecat/processors/audio/audio_buffer_processor.py +78 -37
- pipecat/processors/consumer_processor.py +25 -6
- pipecat/processors/filters/frame_filter.py +23 -0
- pipecat/processors/filters/function_filter.py +30 -0
- pipecat/processors/filters/identity_filter.py +17 -2
- pipecat/processors/filters/null_filter.py +24 -1
- pipecat/processors/filters/stt_mute_filter.py +56 -21
- pipecat/processors/filters/wake_check_filter.py +46 -3
- pipecat/processors/filters/wake_notifier_filter.py +21 -3
- pipecat/processors/frame_processor.py +488 -131
- pipecat/processors/frameworks/langchain.py +38 -3
- pipecat/processors/frameworks/rtvi.py +719 -34
- pipecat/processors/gstreamer/pipeline_source.py +41 -0
- pipecat/processors/idle_frame_processor.py +26 -3
- pipecat/processors/logger.py +23 -0
- pipecat/processors/metrics/frame_processor_metrics.py +77 -4
- pipecat/processors/metrics/sentry.py +42 -4
- pipecat/processors/producer_processor.py +34 -14
- pipecat/processors/text_transformer.py +22 -10
- pipecat/processors/transcript_processor.py +48 -29
- pipecat/processors/user_idle_processor.py +31 -21
- pipecat/runner/__init__.py +1 -0
- pipecat/runner/daily.py +132 -0
- pipecat/runner/livekit.py +148 -0
- pipecat/runner/run.py +543 -0
- pipecat/runner/types.py +67 -0
- pipecat/runner/utils.py +515 -0
- pipecat/serializers/base_serializer.py +42 -0
- pipecat/serializers/exotel.py +17 -6
- pipecat/serializers/genesys.py +95 -0
- pipecat/serializers/livekit.py +33 -0
- pipecat/serializers/plivo.py +16 -15
- pipecat/serializers/protobuf.py +37 -1
- pipecat/serializers/telnyx.py +18 -17
- pipecat/serializers/twilio.py +32 -16
- pipecat/services/ai_service.py +5 -3
- pipecat/services/anthropic/llm.py +113 -43
- pipecat/services/assemblyai/models.py +63 -5
- pipecat/services/assemblyai/stt.py +64 -11
- pipecat/services/asyncai/__init__.py +0 -0
- pipecat/services/asyncai/tts.py +501 -0
- pipecat/services/aws/llm.py +185 -111
- pipecat/services/aws/stt.py +217 -23
- pipecat/services/aws/tts.py +118 -52
- pipecat/services/aws/utils.py +101 -5
- pipecat/services/aws_nova_sonic/aws.py +82 -64
- pipecat/services/aws_nova_sonic/context.py +15 -6
- pipecat/services/azure/common.py +10 -2
- pipecat/services/azure/image.py +32 -0
- pipecat/services/azure/llm.py +9 -7
- pipecat/services/azure/stt.py +65 -2
- pipecat/services/azure/tts.py +154 -23
- pipecat/services/cartesia/stt.py +125 -8
- pipecat/services/cartesia/tts.py +102 -38
- pipecat/services/cerebras/llm.py +15 -23
- pipecat/services/deepgram/stt.py +19 -11
- pipecat/services/deepgram/tts.py +36 -0
- pipecat/services/deepseek/llm.py +14 -23
- pipecat/services/elevenlabs/tts.py +330 -64
- pipecat/services/fal/image.py +43 -0
- pipecat/services/fal/stt.py +48 -10
- pipecat/services/fireworks/llm.py +14 -21
- pipecat/services/fish/tts.py +109 -9
- pipecat/services/gemini_multimodal_live/__init__.py +1 -0
- pipecat/services/gemini_multimodal_live/events.py +83 -2
- pipecat/services/gemini_multimodal_live/file_api.py +189 -0
- pipecat/services/gemini_multimodal_live/gemini.py +218 -21
- pipecat/services/gladia/config.py +17 -10
- pipecat/services/gladia/stt.py +82 -36
- pipecat/services/google/frames.py +40 -0
- pipecat/services/google/google.py +2 -0
- pipecat/services/google/image.py +39 -2
- pipecat/services/google/llm.py +176 -58
- pipecat/services/google/llm_openai.py +26 -4
- pipecat/services/google/llm_vertex.py +37 -15
- pipecat/services/google/rtvi.py +41 -0
- pipecat/services/google/stt.py +65 -17
- pipecat/services/google/test-google-chirp.py +45 -0
- pipecat/services/google/tts.py +390 -19
- pipecat/services/grok/llm.py +8 -6
- pipecat/services/groq/llm.py +8 -6
- pipecat/services/groq/stt.py +13 -9
- pipecat/services/groq/tts.py +40 -0
- pipecat/services/hamsa/__init__.py +9 -0
- pipecat/services/hamsa/stt.py +241 -0
- pipecat/services/heygen/__init__.py +5 -0
- pipecat/services/heygen/api.py +281 -0
- pipecat/services/heygen/client.py +620 -0
- pipecat/services/heygen/video.py +338 -0
- pipecat/services/image_service.py +5 -3
- pipecat/services/inworld/__init__.py +1 -0
- pipecat/services/inworld/tts.py +592 -0
- pipecat/services/llm_service.py +127 -45
- pipecat/services/lmnt/tts.py +80 -7
- pipecat/services/mcp_service.py +85 -44
- pipecat/services/mem0/memory.py +42 -13
- pipecat/services/minimax/tts.py +74 -15
- pipecat/services/mistral/__init__.py +0 -0
- pipecat/services/mistral/llm.py +185 -0
- pipecat/services/moondream/vision.py +55 -10
- pipecat/services/neuphonic/tts.py +275 -48
- pipecat/services/nim/llm.py +8 -6
- pipecat/services/ollama/llm.py +27 -7
- pipecat/services/openai/base_llm.py +54 -16
- pipecat/services/openai/image.py +30 -0
- pipecat/services/openai/llm.py +7 -5
- pipecat/services/openai/stt.py +13 -9
- pipecat/services/openai/tts.py +42 -10
- pipecat/services/openai_realtime_beta/azure.py +11 -9
- pipecat/services/openai_realtime_beta/context.py +7 -5
- pipecat/services/openai_realtime_beta/events.py +10 -7
- pipecat/services/openai_realtime_beta/openai.py +37 -18
- pipecat/services/openpipe/llm.py +30 -24
- pipecat/services/openrouter/llm.py +9 -7
- pipecat/services/perplexity/llm.py +15 -19
- pipecat/services/piper/tts.py +26 -12
- pipecat/services/playht/tts.py +227 -65
- pipecat/services/qwen/llm.py +8 -6
- pipecat/services/rime/tts.py +128 -17
- pipecat/services/riva/stt.py +160 -22
- pipecat/services/riva/tts.py +67 -2
- pipecat/services/sambanova/llm.py +19 -17
- pipecat/services/sambanova/stt.py +14 -8
- pipecat/services/sarvam/tts.py +60 -13
- pipecat/services/simli/video.py +82 -21
- pipecat/services/soniox/__init__.py +0 -0
- pipecat/services/soniox/stt.py +398 -0
- pipecat/services/speechmatics/stt.py +29 -17
- pipecat/services/stt_service.py +47 -11
- pipecat/services/tavus/video.py +94 -25
- pipecat/services/together/llm.py +8 -6
- pipecat/services/tts_service.py +77 -53
- pipecat/services/ultravox/stt.py +46 -43
- pipecat/services/vision_service.py +5 -3
- pipecat/services/websocket_service.py +12 -11
- pipecat/services/whisper/base_stt.py +58 -12
- pipecat/services/whisper/stt.py +69 -58
- pipecat/services/xtts/tts.py +59 -2
- pipecat/sync/base_notifier.py +19 -0
- pipecat/sync/event_notifier.py +24 -0
- pipecat/tests/utils.py +73 -5
- pipecat/transcriptions/language.py +24 -0
- pipecat/transports/base_input.py +112 -8
- pipecat/transports/base_output.py +235 -13
- pipecat/transports/base_transport.py +119 -0
- pipecat/transports/local/audio.py +76 -0
- pipecat/transports/local/tk.py +84 -0
- pipecat/transports/network/fastapi_websocket.py +174 -15
- pipecat/transports/network/small_webrtc.py +383 -39
- pipecat/transports/network/webrtc_connection.py +214 -8
- pipecat/transports/network/websocket_client.py +171 -1
- pipecat/transports/network/websocket_server.py +147 -9
- pipecat/transports/services/daily.py +792 -70
- pipecat/transports/services/helpers/daily_rest.py +122 -129
- pipecat/transports/services/livekit.py +339 -4
- pipecat/transports/services/tavus.py +273 -38
- pipecat/utils/asyncio/task_manager.py +92 -186
- pipecat/utils/base_object.py +83 -1
- pipecat/utils/network.py +2 -0
- pipecat/utils/string.py +114 -58
- pipecat/utils/text/base_text_aggregator.py +44 -13
- pipecat/utils/text/base_text_filter.py +46 -0
- pipecat/utils/text/markdown_text_filter.py +70 -14
- pipecat/utils/text/pattern_pair_aggregator.py +18 -14
- pipecat/utils/text/simple_text_aggregator.py +43 -2
- pipecat/utils/text/skip_tags_aggregator.py +21 -13
- pipecat/utils/time.py +36 -0
- pipecat/utils/tracing/class_decorators.py +32 -7
- pipecat/utils/tracing/conversation_context_provider.py +12 -2
- pipecat/utils/tracing/service_attributes.py +80 -64
- pipecat/utils/tracing/service_decorators.py +48 -21
- pipecat/utils/tracing/setup.py +13 -7
- pipecat/utils/tracing/turn_context_provider.py +12 -2
- pipecat/utils/tracing/turn_trace_observer.py +27 -0
- pipecat/utils/utils.py +14 -14
- dv_pipecat_ai-0.0.74.dev770.dist-info/RECORD +0 -319
- pipecat/examples/daily_runner.py +0 -64
- pipecat/examples/run.py +0 -265
- pipecat/utils/asyncio/watchdog_async_iterator.py +0 -72
- pipecat/utils/asyncio/watchdog_event.py +0 -42
- pipecat/utils/asyncio/watchdog_priority_queue.py +0 -48
- pipecat/utils/asyncio/watchdog_queue.py +0 -48
- {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/WHEEL +0 -0
- {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/licenses/LICENSE +0 -0
- {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/top_level.txt +0 -0
- /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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
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
|
-
|
|
297
|
-
return
|
|
426
|
+
"""Write an audio frame to the WebSocket with timing simulation.
|
|
298
427
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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)
|