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 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
|
|
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)
|