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,13 @@
|
|
|
4
4
|
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
5
|
#
|
|
6
6
|
|
|
7
|
+
"""WebSocket server transport implementation for Pipecat.
|
|
8
|
+
|
|
9
|
+
This module provides WebSocket server transport functionality for real-time
|
|
10
|
+
audio and data streaming, including client connection management, session
|
|
11
|
+
handling, and frame serialization.
|
|
12
|
+
"""
|
|
13
|
+
|
|
7
14
|
import asyncio
|
|
8
15
|
import io
|
|
9
16
|
import time
|
|
@@ -32,6 +39,8 @@ from pipecat.transports.base_transport import BaseTransport, TransportParams
|
|
|
32
39
|
|
|
33
40
|
try:
|
|
34
41
|
import websockets
|
|
42
|
+
from websockets.asyncio.server import serve as websocket_serve
|
|
43
|
+
from websockets.protocol import State
|
|
35
44
|
except ModuleNotFoundError as e:
|
|
36
45
|
logger.error(f"Exception: {e}")
|
|
37
46
|
logger.error("In order to use websockets, you need to `pip install pipecat-ai[websocket]`.")
|
|
@@ -39,12 +48,29 @@ except ModuleNotFoundError as e:
|
|
|
39
48
|
|
|
40
49
|
|
|
41
50
|
class WebsocketServerParams(TransportParams):
|
|
51
|
+
"""Configuration parameters for WebSocket server transport.
|
|
52
|
+
|
|
53
|
+
Parameters:
|
|
54
|
+
add_wav_header: Whether to add WAV headers to audio frames.
|
|
55
|
+
serializer: Frame serializer for message encoding/decoding.
|
|
56
|
+
session_timeout: Timeout in seconds for client sessions.
|
|
57
|
+
"""
|
|
58
|
+
|
|
42
59
|
add_wav_header: bool = False
|
|
43
60
|
serializer: Optional[FrameSerializer] = None
|
|
44
61
|
session_timeout: Optional[int] = None
|
|
45
62
|
|
|
46
63
|
|
|
47
64
|
class WebsocketServerCallbacks(BaseModel):
|
|
65
|
+
"""Callback functions for WebSocket server events.
|
|
66
|
+
|
|
67
|
+
Parameters:
|
|
68
|
+
on_client_connected: Called when a client connects to the server.
|
|
69
|
+
on_client_disconnected: Called when a client disconnects from the server.
|
|
70
|
+
on_session_timeout: Called when a client session times out.
|
|
71
|
+
on_websocket_ready: Called when the WebSocket server is ready to accept connections.
|
|
72
|
+
"""
|
|
73
|
+
|
|
48
74
|
on_client_connected: Callable[[websockets.WebSocketServerProtocol], Awaitable[None]]
|
|
49
75
|
on_client_disconnected: Callable[[websockets.WebSocketServerProtocol], Awaitable[None]]
|
|
50
76
|
on_session_timeout: Callable[[websockets.WebSocketServerProtocol], Awaitable[None]]
|
|
@@ -52,6 +78,12 @@ class WebsocketServerCallbacks(BaseModel):
|
|
|
52
78
|
|
|
53
79
|
|
|
54
80
|
class WebsocketServerInputTransport(BaseInputTransport):
|
|
81
|
+
"""WebSocket server input transport for receiving client data.
|
|
82
|
+
|
|
83
|
+
Handles incoming WebSocket connections, message processing, and client
|
|
84
|
+
session management including timeout monitoring and connection lifecycle.
|
|
85
|
+
"""
|
|
86
|
+
|
|
55
87
|
def __init__(
|
|
56
88
|
self,
|
|
57
89
|
transport: BaseTransport,
|
|
@@ -61,6 +93,16 @@ class WebsocketServerInputTransport(BaseInputTransport):
|
|
|
61
93
|
callbacks: WebsocketServerCallbacks,
|
|
62
94
|
**kwargs,
|
|
63
95
|
):
|
|
96
|
+
"""Initialize the WebSocket server input transport.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
transport: The parent transport instance.
|
|
100
|
+
host: Host address to bind the WebSocket server to.
|
|
101
|
+
port: Port number to bind the WebSocket server to.
|
|
102
|
+
params: WebSocket server configuration parameters.
|
|
103
|
+
callbacks: Callback functions for WebSocket events.
|
|
104
|
+
**kwargs: Additional arguments passed to parent class.
|
|
105
|
+
"""
|
|
64
106
|
super().__init__(params, **kwargs)
|
|
65
107
|
|
|
66
108
|
self._transport = transport
|
|
@@ -82,6 +124,11 @@ class WebsocketServerInputTransport(BaseInputTransport):
|
|
|
82
124
|
self._initialized = False
|
|
83
125
|
|
|
84
126
|
async def start(self, frame: StartFrame):
|
|
127
|
+
"""Start the WebSocket server and initialize components.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
frame: The start frame containing initialization parameters.
|
|
131
|
+
"""
|
|
85
132
|
await super().start(frame)
|
|
86
133
|
|
|
87
134
|
if self._initialized:
|
|
@@ -96,16 +143,26 @@ class WebsocketServerInputTransport(BaseInputTransport):
|
|
|
96
143
|
await self.set_transport_ready(frame)
|
|
97
144
|
|
|
98
145
|
async def stop(self, frame: EndFrame):
|
|
146
|
+
"""Stop the WebSocket server and cleanup resources.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
frame: The end frame signaling transport shutdown.
|
|
150
|
+
"""
|
|
99
151
|
await super().stop(frame)
|
|
100
152
|
self._stop_server_event.set()
|
|
101
153
|
if self._monitor_task:
|
|
102
154
|
await self.cancel_task(self._monitor_task)
|
|
103
155
|
self._monitor_task = None
|
|
104
156
|
if self._server_task:
|
|
105
|
-
await self.
|
|
157
|
+
await self._server_task
|
|
106
158
|
self._server_task = None
|
|
107
159
|
|
|
108
160
|
async def cancel(self, frame: CancelFrame):
|
|
161
|
+
"""Cancel the WebSocket server and stop all processing.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
frame: The cancel frame signaling immediate cancellation.
|
|
165
|
+
"""
|
|
109
166
|
await super().cancel(frame)
|
|
110
167
|
if self._monitor_task:
|
|
111
168
|
await self.cancel_task(self._monitor_task)
|
|
@@ -115,16 +172,19 @@ class WebsocketServerInputTransport(BaseInputTransport):
|
|
|
115
172
|
self._server_task = None
|
|
116
173
|
|
|
117
174
|
async def cleanup(self):
|
|
175
|
+
"""Cleanup resources and parent transport."""
|
|
118
176
|
await super().cleanup()
|
|
119
177
|
await self._transport.cleanup()
|
|
120
178
|
|
|
121
179
|
async def _server_task_handler(self):
|
|
180
|
+
"""Handle WebSocket server startup and client connections."""
|
|
122
181
|
logger.info(f"Starting websocket server on {self._host}:{self._port}")
|
|
123
|
-
async with
|
|
182
|
+
async with websocket_serve(self._client_handler, self._host, self._port) as server:
|
|
124
183
|
await self._callbacks.on_websocket_ready()
|
|
125
184
|
await self._stop_server_event.wait()
|
|
126
185
|
|
|
127
|
-
async def _client_handler(self, websocket: websockets.WebSocketServerProtocol
|
|
186
|
+
async def _client_handler(self, websocket: websockets.WebSocketServerProtocol):
|
|
187
|
+
"""Handle individual client connections and message processing."""
|
|
128
188
|
logger.info(f"New client connection from {websocket.remote_address}")
|
|
129
189
|
if self._websocket:
|
|
130
190
|
await self._websocket.close()
|
|
@@ -170,12 +230,10 @@ class WebsocketServerInputTransport(BaseInputTransport):
|
|
|
170
230
|
async def _monitor_websocket(
|
|
171
231
|
self, websocket: websockets.WebSocketServerProtocol, session_timeout: int
|
|
172
232
|
):
|
|
173
|
-
"""
|
|
174
|
-
trigger timeout event.
|
|
175
|
-
"""
|
|
233
|
+
"""Monitor WebSocket connection for session timeout."""
|
|
176
234
|
try:
|
|
177
235
|
await asyncio.sleep(session_timeout)
|
|
178
|
-
if not
|
|
236
|
+
if websocket.state is not State.CLOSED:
|
|
179
237
|
await self._callbacks.on_session_timeout(websocket)
|
|
180
238
|
except asyncio.CancelledError:
|
|
181
239
|
logger.info(f"Monitoring task cancelled for: {websocket.remote_address}")
|
|
@@ -183,7 +241,20 @@ class WebsocketServerInputTransport(BaseInputTransport):
|
|
|
183
241
|
|
|
184
242
|
|
|
185
243
|
class WebsocketServerOutputTransport(BaseOutputTransport):
|
|
244
|
+
"""WebSocket server output transport for sending data to clients.
|
|
245
|
+
|
|
246
|
+
Handles outgoing frame serialization, audio streaming with timing control,
|
|
247
|
+
and client connection management for WebSocket communication.
|
|
248
|
+
"""
|
|
249
|
+
|
|
186
250
|
def __init__(self, transport: BaseTransport, params: WebsocketServerParams, **kwargs):
|
|
251
|
+
"""Initialize the WebSocket server output transport.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
transport: The parent transport instance.
|
|
255
|
+
params: WebSocket server configuration parameters.
|
|
256
|
+
**kwargs: Additional arguments passed to parent class.
|
|
257
|
+
"""
|
|
187
258
|
super().__init__(params, **kwargs)
|
|
188
259
|
|
|
189
260
|
self._transport = transport
|
|
@@ -203,12 +274,22 @@ class WebsocketServerOutputTransport(BaseOutputTransport):
|
|
|
203
274
|
self._initialized = False
|
|
204
275
|
|
|
205
276
|
async def set_client_connection(self, websocket: Optional[websockets.WebSocketServerProtocol]):
|
|
277
|
+
"""Set the active client WebSocket connection.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
websocket: The WebSocket connection to set as active, or None to clear.
|
|
281
|
+
"""
|
|
206
282
|
if self._websocket:
|
|
207
283
|
await self._websocket.close()
|
|
208
284
|
logger.warning("Only one client allowed, using new connection")
|
|
209
285
|
self._websocket = websocket
|
|
210
286
|
|
|
211
287
|
async def start(self, frame: StartFrame):
|
|
288
|
+
"""Start the output transport and initialize components.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
frame: The start frame containing initialization parameters.
|
|
292
|
+
"""
|
|
212
293
|
await super().start(frame)
|
|
213
294
|
|
|
214
295
|
if self._initialized:
|
|
@@ -222,18 +303,35 @@ class WebsocketServerOutputTransport(BaseOutputTransport):
|
|
|
222
303
|
await self.set_transport_ready(frame)
|
|
223
304
|
|
|
224
305
|
async def stop(self, frame: EndFrame):
|
|
306
|
+
"""Stop the output transport and send final frame.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
frame: The end frame signaling transport shutdown.
|
|
310
|
+
"""
|
|
225
311
|
await super().stop(frame)
|
|
226
312
|
await self._write_frame(frame)
|
|
227
313
|
|
|
228
314
|
async def cancel(self, frame: CancelFrame):
|
|
315
|
+
"""Cancel the output transport and send cancellation frame.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
frame: The cancel frame signaling immediate cancellation.
|
|
319
|
+
"""
|
|
229
320
|
await super().cancel(frame)
|
|
230
321
|
await self._write_frame(frame)
|
|
231
322
|
|
|
232
323
|
async def cleanup(self):
|
|
324
|
+
"""Cleanup resources and parent transport."""
|
|
233
325
|
await super().cleanup()
|
|
234
326
|
await self._transport.cleanup()
|
|
235
327
|
|
|
236
328
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
|
329
|
+
"""Process frames and handle interruption timing.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
frame: The frame to process.
|
|
333
|
+
direction: The direction of frame flow in the pipeline.
|
|
334
|
+
"""
|
|
237
335
|
await super().process_frame(frame, direction)
|
|
238
336
|
|
|
239
337
|
if isinstance(frame, StartInterruptionFrame):
|
|
@@ -241,12 +339,20 @@ class WebsocketServerOutputTransport(BaseOutputTransport):
|
|
|
241
339
|
self._next_send_time = 0
|
|
242
340
|
|
|
243
341
|
async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame):
|
|
342
|
+
"""Send a transport message frame to the client.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
frame: The transport message frame to send.
|
|
346
|
+
"""
|
|
244
347
|
await self._write_frame(frame)
|
|
245
348
|
|
|
246
349
|
async def write_audio_frame(self, frame: OutputAudioRawFrame):
|
|
350
|
+
"""Write an audio frame to the WebSocket client with timing control.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
frame: The output audio frame to write.
|
|
354
|
+
"""
|
|
247
355
|
if not self._websocket:
|
|
248
|
-
# Simulate audio playback with a sleep.
|
|
249
|
-
await self._write_audio_sleep()
|
|
250
356
|
return
|
|
251
357
|
|
|
252
358
|
frame = OutputAudioRawFrame(
|
|
@@ -275,6 +381,7 @@ class WebsocketServerOutputTransport(BaseOutputTransport):
|
|
|
275
381
|
await self._write_audio_sleep()
|
|
276
382
|
|
|
277
383
|
async def _write_frame(self, frame: Frame):
|
|
384
|
+
"""Serialize and send a frame to the WebSocket client."""
|
|
278
385
|
if not self._params.serializer:
|
|
279
386
|
return
|
|
280
387
|
|
|
@@ -286,6 +393,7 @@ class WebsocketServerOutputTransport(BaseOutputTransport):
|
|
|
286
393
|
logger.error(f"{self} exception sending data: {e.__class__.__name__} ({e})")
|
|
287
394
|
|
|
288
395
|
async def _write_audio_sleep(self):
|
|
396
|
+
"""Simulate audio device timing by sleeping between audio chunks."""
|
|
289
397
|
# Simulate a clock.
|
|
290
398
|
current_time = time.monotonic()
|
|
291
399
|
sleep_duration = max(0, self._next_send_time - current_time)
|
|
@@ -297,6 +405,13 @@ class WebsocketServerOutputTransport(BaseOutputTransport):
|
|
|
297
405
|
|
|
298
406
|
|
|
299
407
|
class WebsocketServerTransport(BaseTransport):
|
|
408
|
+
"""WebSocket server transport for bidirectional real-time communication.
|
|
409
|
+
|
|
410
|
+
Provides a complete WebSocket server implementation with separate input and
|
|
411
|
+
output transports, client connection management, and event handling for
|
|
412
|
+
real-time audio and data streaming applications.
|
|
413
|
+
"""
|
|
414
|
+
|
|
300
415
|
def __init__(
|
|
301
416
|
self,
|
|
302
417
|
params: WebsocketServerParams,
|
|
@@ -305,6 +420,15 @@ class WebsocketServerTransport(BaseTransport):
|
|
|
305
420
|
input_name: Optional[str] = None,
|
|
306
421
|
output_name: Optional[str] = None,
|
|
307
422
|
):
|
|
423
|
+
"""Initialize the WebSocket server transport.
|
|
424
|
+
|
|
425
|
+
Args:
|
|
426
|
+
params: WebSocket server configuration parameters.
|
|
427
|
+
host: Host address to bind the server to. Defaults to "localhost".
|
|
428
|
+
port: Port number to bind the server to. Defaults to 8765.
|
|
429
|
+
input_name: Optional name for the input processor.
|
|
430
|
+
output_name: Optional name for the output processor.
|
|
431
|
+
"""
|
|
308
432
|
super().__init__(input_name=input_name, output_name=output_name)
|
|
309
433
|
self._host = host
|
|
310
434
|
self._port = port
|
|
@@ -328,6 +452,11 @@ class WebsocketServerTransport(BaseTransport):
|
|
|
328
452
|
self._register_event_handler("on_websocket_ready")
|
|
329
453
|
|
|
330
454
|
def input(self) -> WebsocketServerInputTransport:
|
|
455
|
+
"""Get the input transport for receiving client data.
|
|
456
|
+
|
|
457
|
+
Returns:
|
|
458
|
+
The WebSocket server input transport instance.
|
|
459
|
+
"""
|
|
331
460
|
if not self._input:
|
|
332
461
|
self._input = WebsocketServerInputTransport(
|
|
333
462
|
self, self._host, self._port, self._params, self._callbacks, name=self._input_name
|
|
@@ -335,6 +464,11 @@ class WebsocketServerTransport(BaseTransport):
|
|
|
335
464
|
return self._input
|
|
336
465
|
|
|
337
466
|
def output(self) -> WebsocketServerOutputTransport:
|
|
467
|
+
"""Get the output transport for sending data to clients.
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
The WebSocket server output transport instance.
|
|
471
|
+
"""
|
|
338
472
|
if not self._output:
|
|
339
473
|
self._output = WebsocketServerOutputTransport(
|
|
340
474
|
self, self._params, name=self._output_name
|
|
@@ -342,6 +476,7 @@ class WebsocketServerTransport(BaseTransport):
|
|
|
342
476
|
return self._output
|
|
343
477
|
|
|
344
478
|
async def _on_client_connected(self, websocket):
|
|
479
|
+
"""Handle client connection events."""
|
|
345
480
|
if self._output:
|
|
346
481
|
await self._output.set_client_connection(websocket)
|
|
347
482
|
await self._call_event_handler("on_client_connected", websocket)
|
|
@@ -349,6 +484,7 @@ class WebsocketServerTransport(BaseTransport):
|
|
|
349
484
|
logger.error("A WebsocketServerTransport output is missing in the pipeline")
|
|
350
485
|
|
|
351
486
|
async def _on_client_disconnected(self, websocket):
|
|
487
|
+
"""Handle client disconnection events."""
|
|
352
488
|
if self._output:
|
|
353
489
|
await self._output.set_client_connection(None)
|
|
354
490
|
await self._call_event_handler("on_client_disconnected", websocket)
|
|
@@ -356,7 +492,9 @@ class WebsocketServerTransport(BaseTransport):
|
|
|
356
492
|
logger.error("A WebsocketServerTransport output is missing in the pipeline")
|
|
357
493
|
|
|
358
494
|
async def _on_session_timeout(self, websocket):
|
|
495
|
+
"""Handle client session timeout events."""
|
|
359
496
|
await self._call_event_handler("on_session_timeout", websocket)
|
|
360
497
|
|
|
361
498
|
async def _on_websocket_ready(self):
|
|
499
|
+
"""Handle WebSocket server ready events."""
|
|
362
500
|
await self._call_event_handler("on_websocket_ready")
|