dv-pipecat-ai 0.0.85.dev818__py3-none-any.whl → 0.0.85.dev858__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.85.dev818.dist-info → dv_pipecat_ai-0.0.85.dev858.dist-info}/METADATA +2 -1
- {dv_pipecat_ai-0.0.85.dev818.dist-info → dv_pipecat_ai-0.0.85.dev858.dist-info}/RECORD +32 -29
- pipecat/audio/turn/smart_turn/local_smart_turn_v3.py +5 -1
- pipecat/frames/frames.py +34 -0
- pipecat/metrics/connection_metrics.py +45 -0
- pipecat/processors/aggregators/llm_response.py +25 -4
- pipecat/processors/dtmf_aggregator.py +17 -21
- pipecat/processors/frame_processor.py +51 -8
- pipecat/processors/metrics/frame_processor_metrics.py +108 -0
- pipecat/processors/transcript_processor.py +22 -1
- pipecat/serializers/__init__.py +2 -0
- pipecat/serializers/asterisk.py +16 -2
- pipecat/serializers/convox.py +2 -2
- pipecat/serializers/custom.py +2 -2
- pipecat/serializers/vi.py +326 -0
- pipecat/services/cartesia/tts.py +75 -10
- pipecat/services/deepgram/stt.py +317 -17
- pipecat/services/elevenlabs/stt.py +487 -19
- pipecat/services/elevenlabs/tts.py +28 -4
- pipecat/services/google/llm.py +26 -11
- pipecat/services/openai/base_llm.py +79 -14
- pipecat/services/salesforce/llm.py +321 -86
- pipecat/services/sarvam/tts.py +0 -1
- pipecat/services/soniox/stt.py +45 -10
- pipecat/services/vistaar/llm.py +97 -6
- pipecat/transcriptions/language.py +50 -0
- pipecat/transports/base_input.py +15 -11
- pipecat/transports/base_output.py +29 -3
- pipecat/utils/redis.py +58 -0
- {dv_pipecat_ai-0.0.85.dev818.dist-info → dv_pipecat_ai-0.0.85.dev858.dist-info}/WHEEL +0 -0
- {dv_pipecat_ai-0.0.85.dev818.dist-info → dv_pipecat_ai-0.0.85.dev858.dist-info}/licenses/LICENSE +0 -0
- {dv_pipecat_ai-0.0.85.dev818.dist-info → dv_pipecat_ai-0.0.85.dev858.dist-info}/top_level.txt +0 -0
|
@@ -436,10 +436,53 @@ class FrameProcessor(BaseObject):
|
|
|
436
436
|
if frame:
|
|
437
437
|
await self.push_frame(frame)
|
|
438
438
|
|
|
439
|
+
async def start_connection_metrics(self):
|
|
440
|
+
"""Start connection establishment metrics collection."""
|
|
441
|
+
if self.can_generate_metrics() and self.metrics_enabled:
|
|
442
|
+
await self._metrics.start_connection_metrics()
|
|
443
|
+
|
|
444
|
+
async def stop_connection_metrics(
|
|
445
|
+
self,
|
|
446
|
+
success: bool = True,
|
|
447
|
+
error: str = None,
|
|
448
|
+
connection_type: str = None
|
|
449
|
+
):
|
|
450
|
+
"""Stop connection metrics collection and emit metrics frame.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
success: Whether the connection was successful.
|
|
454
|
+
error: Error message if connection failed.
|
|
455
|
+
connection_type: Type of connection (websocket, http, etc.).
|
|
456
|
+
"""
|
|
457
|
+
if self.can_generate_metrics() and self.metrics_enabled:
|
|
458
|
+
frame = await self._metrics.stop_connection_metrics(success, error, connection_type)
|
|
459
|
+
if frame:
|
|
460
|
+
await self.push_frame(frame)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
async def start_reconnection_metrics(self):
|
|
464
|
+
"""Start reconnection metrics collection."""
|
|
465
|
+
if self.can_generate_metrics() and self.metrics_enabled:
|
|
466
|
+
await self._metrics.start_reconnection_metrics()
|
|
467
|
+
|
|
468
|
+
async def stop_reconnection_metrics(self, success: bool = True, reason: str = None):
|
|
469
|
+
"""Stop reconnection metrics collection and emit metrics frame.
|
|
470
|
+
|
|
471
|
+
Args:
|
|
472
|
+
success: Whether the reconnection was successful.
|
|
473
|
+
reason: Reason for reconnection.
|
|
474
|
+
"""
|
|
475
|
+
if self.can_generate_metrics() and self.metrics_enabled:
|
|
476
|
+
frame = await self._metrics.stop_reconnection_metrics(success, reason)
|
|
477
|
+
if frame:
|
|
478
|
+
await self.push_frame(frame)
|
|
479
|
+
|
|
480
|
+
|
|
439
481
|
async def stop_all_metrics(self):
|
|
440
482
|
"""Stop all active metrics collection."""
|
|
441
483
|
await self.stop_ttfb_metrics()
|
|
442
484
|
await self.stop_processing_metrics()
|
|
485
|
+
await self.stop_connection_metrics()
|
|
443
486
|
|
|
444
487
|
def create_task(self, coroutine: Coroutine, name: Optional[str] = None) -> asyncio.Task:
|
|
445
488
|
"""Create a new task managed by this processor.
|
|
@@ -591,7 +634,7 @@ class FrameProcessor(BaseObject):
|
|
|
591
634
|
|
|
592
635
|
async def pause_processing_system_frames(self):
|
|
593
636
|
"""Pause processing of queued system frames."""
|
|
594
|
-
logger.trace(f"{self}: pausing system frame processing")
|
|
637
|
+
self.logger.trace(f"{self}: pausing system frame processing")
|
|
595
638
|
self.__should_block_system_frames = True
|
|
596
639
|
if self.__input_event:
|
|
597
640
|
self.__input_event.clear()
|
|
@@ -811,8 +854,8 @@ class FrameProcessor(BaseObject):
|
|
|
811
854
|
Returns:
|
|
812
855
|
True if the processor has been started.
|
|
813
856
|
"""
|
|
814
|
-
if not self.__started:
|
|
815
|
-
logger.error(f"{self} Trying to process {frame} but StartFrame not received yet")
|
|
857
|
+
if not self.__started and not isinstance(frame, SystemFrame):
|
|
858
|
+
self.logger.error(f"{self} Trying to process {frame} but StartFrame not received yet")
|
|
816
859
|
return self.__started
|
|
817
860
|
|
|
818
861
|
def __create_input_task(self):
|
|
@@ -876,7 +919,7 @@ class FrameProcessor(BaseObject):
|
|
|
876
919
|
|
|
877
920
|
await self._call_event_handler("on_after_process_frame", frame)
|
|
878
921
|
except Exception as e:
|
|
879
|
-
logger.exception(f"{self}: error processing frame: {e}")
|
|
922
|
+
self.logger.exception(f"{self}: error processing frame: {e}")
|
|
880
923
|
await self.push_error(ErrorFrame(str(e)))
|
|
881
924
|
|
|
882
925
|
async def __input_frame_task_handler(self):
|
|
@@ -890,11 +933,11 @@ class FrameProcessor(BaseObject):
|
|
|
890
933
|
(frame, direction, callback) = await self.__input_queue.get()
|
|
891
934
|
|
|
892
935
|
if self.__should_block_system_frames and self.__input_event:
|
|
893
|
-
logger.trace(f"{self}: system frame processing paused")
|
|
936
|
+
self.logger.trace(f"{self}: system frame processing paused")
|
|
894
937
|
await self.__input_event.wait()
|
|
895
938
|
self.__input_event.clear()
|
|
896
939
|
self.__should_block_system_frames = False
|
|
897
|
-
logger.trace(f"{self}: system frame processing resumed")
|
|
940
|
+
self.logger.trace(f"{self}: system frame processing resumed")
|
|
898
941
|
|
|
899
942
|
if isinstance(frame, SystemFrame):
|
|
900
943
|
await self.__process_frame(frame, direction, callback)
|
|
@@ -913,11 +956,11 @@ class FrameProcessor(BaseObject):
|
|
|
913
956
|
(frame, direction, callback) = await self.__process_queue.get()
|
|
914
957
|
|
|
915
958
|
if self.__should_block_frames and self.__process_event:
|
|
916
|
-
logger.trace(f"{self}: frame processing paused")
|
|
959
|
+
self.logger.trace(f"{self}: frame processing paused")
|
|
917
960
|
await self.__process_event.wait()
|
|
918
961
|
self.__process_event.clear()
|
|
919
962
|
self.__should_block_frames = False
|
|
920
|
-
logger.trace(f"{self}: frame processing resumed")
|
|
963
|
+
self.logger.trace(f"{self}: frame processing resumed")
|
|
921
964
|
|
|
922
965
|
await self.__process_frame(frame, direction, callback)
|
|
923
966
|
|
|
@@ -20,6 +20,9 @@ from pipecat.metrics.metrics import (
|
|
|
20
20
|
TTFBMetricsData,
|
|
21
21
|
TTSUsageMetricsData,
|
|
22
22
|
)
|
|
23
|
+
from pipecat.metrics.connection_metrics import (
|
|
24
|
+
ConnectionMetricsData,
|
|
25
|
+
)
|
|
23
26
|
from pipecat.utils.asyncio.task_manager import BaseTaskManager
|
|
24
27
|
from pipecat.utils.base_object import BaseObject
|
|
25
28
|
|
|
@@ -46,6 +49,13 @@ class FrameProcessorMetrics(BaseObject):
|
|
|
46
49
|
self._last_ttfb_time = 0
|
|
47
50
|
self._should_report_ttfb = True
|
|
48
51
|
self._logger = logger
|
|
52
|
+
|
|
53
|
+
# Connection metrics state
|
|
54
|
+
self._start_connection_time = 0
|
|
55
|
+
self._connection_attempts = 0
|
|
56
|
+
self._last_connection_error = None
|
|
57
|
+
self._reconnection_start_time = 0
|
|
58
|
+
self._reconnect_count = 0
|
|
49
59
|
|
|
50
60
|
async def setup(self, task_manager: BaseTaskManager):
|
|
51
61
|
"""Set up the metrics collector with a task manager.
|
|
@@ -195,3 +205,101 @@ class FrameProcessorMetrics(BaseObject):
|
|
|
195
205
|
)
|
|
196
206
|
self._logger.debug(f"{self._processor_name()} usage characters: {characters.value}")
|
|
197
207
|
return MetricsFrame(data=[characters])
|
|
208
|
+
|
|
209
|
+
async def start_connection_metrics(self):
|
|
210
|
+
"""Start measuring connection establishment time."""
|
|
211
|
+
self._start_connection_time = time.time()
|
|
212
|
+
self._connection_attempts += 1
|
|
213
|
+
self._last_connection_error = None
|
|
214
|
+
|
|
215
|
+
async def stop_connection_metrics(
|
|
216
|
+
self,
|
|
217
|
+
success: bool = True,
|
|
218
|
+
error: str = None,
|
|
219
|
+
connection_type: str = None
|
|
220
|
+
):
|
|
221
|
+
"""Stop connection measurement and generate metrics frame.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
success: Whether the connection was successful.
|
|
225
|
+
error: Error message if connection failed.
|
|
226
|
+
connection_type: Type of connection (websocket, http, etc.).
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
MetricsFrame containing connection data, or None if not measuring.
|
|
230
|
+
"""
|
|
231
|
+
if self._start_connection_time == 0:
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
connect_time = time.time() - self._start_connection_time
|
|
235
|
+
|
|
236
|
+
if not success:
|
|
237
|
+
self._last_connection_error = error
|
|
238
|
+
|
|
239
|
+
logstr = f"{self._processor_name()} connection "
|
|
240
|
+
logstr += "successful" if success else f"failed: {error}"
|
|
241
|
+
logstr += f" (attempt #{self._connection_attempts}, {connect_time:.3f}s)"
|
|
242
|
+
|
|
243
|
+
if success:
|
|
244
|
+
self._logger.debug(logstr)
|
|
245
|
+
else:
|
|
246
|
+
self._logger.warning(logstr)
|
|
247
|
+
|
|
248
|
+
connection_data = ConnectionMetricsData(
|
|
249
|
+
processor=self._processor_name(),
|
|
250
|
+
model=self._model_name(),
|
|
251
|
+
connect_time=round(connect_time, 3),
|
|
252
|
+
success=success,
|
|
253
|
+
connection_attempts=self._connection_attempts,
|
|
254
|
+
error_message=error,
|
|
255
|
+
connection_type=connection_type
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
self._start_connection_time = 0
|
|
259
|
+
return MetricsFrame(data=[connection_data])
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
async def start_reconnection_metrics(self):
|
|
263
|
+
"""Start measuring reconnection downtime."""
|
|
264
|
+
self._reconnection_start_time = time.time()
|
|
265
|
+
self._reconnect_count += 1
|
|
266
|
+
|
|
267
|
+
async def stop_reconnection_metrics(
|
|
268
|
+
self,
|
|
269
|
+
success: bool = True,
|
|
270
|
+
reason: str = None
|
|
271
|
+
):
|
|
272
|
+
"""Stop reconnection measurement and generate metrics frame.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
success: Whether the reconnection was successful.
|
|
276
|
+
reason: Reason for reconnection.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
MetricsFrame containing reconnection data, or None if not measuring.
|
|
280
|
+
"""
|
|
281
|
+
if self._reconnection_start_time == 0:
|
|
282
|
+
return None
|
|
283
|
+
|
|
284
|
+
downtime = time.time() - self._reconnection_start_time
|
|
285
|
+
|
|
286
|
+
logstr = f"{self._processor_name()} reconnection #{self._reconnect_count} "
|
|
287
|
+
logstr += "successful" if success else "failed"
|
|
288
|
+
logstr += f" (downtime: {downtime:.3f}s)"
|
|
289
|
+
if reason:
|
|
290
|
+
logstr += f" - {reason}"
|
|
291
|
+
|
|
292
|
+
self._logger.debug(logstr)
|
|
293
|
+
|
|
294
|
+
reconnection_data = ConnectionMetricsData(
|
|
295
|
+
processor=self._processor_name(),
|
|
296
|
+
model=self._model_name(),
|
|
297
|
+
reconnect_count=self._reconnect_count,
|
|
298
|
+
downtime=round(downtime, 3),
|
|
299
|
+
reconnect_success=success,
|
|
300
|
+
reason=reason
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
self._reconnection_start_time = 0
|
|
304
|
+
return MetricsFrame(data=[reconnection_data])
|
|
305
|
+
|
|
@@ -20,6 +20,7 @@ from pipecat.frames.frames import (
|
|
|
20
20
|
EndFrame,
|
|
21
21
|
Frame,
|
|
22
22
|
InterruptionFrame,
|
|
23
|
+
TranscriptDropFrame,
|
|
23
24
|
TranscriptionFrame,
|
|
24
25
|
TranscriptionMessage,
|
|
25
26
|
TranscriptionUpdateFrame,
|
|
@@ -44,6 +45,7 @@ class BaseTranscriptProcessor(FrameProcessor):
|
|
|
44
45
|
super().__init__(**kwargs)
|
|
45
46
|
self._processed_messages: List[TranscriptionMessage] = []
|
|
46
47
|
self._register_event_handler("on_transcript_update")
|
|
48
|
+
self._register_event_handler("on_transcript_drop")
|
|
47
49
|
|
|
48
50
|
async def _emit_update(self, messages: List[TranscriptionMessage]):
|
|
49
51
|
"""Emit transcript updates for new messages.
|
|
@@ -57,6 +59,19 @@ class BaseTranscriptProcessor(FrameProcessor):
|
|
|
57
59
|
await self._call_event_handler("on_transcript_update", update_frame)
|
|
58
60
|
await self.push_frame(update_frame)
|
|
59
61
|
|
|
62
|
+
async def _handle_transcript_drop(self, frame: TranscriptDropFrame):
|
|
63
|
+
"""Handle transcript drop notifications by removing stored messages."""
|
|
64
|
+
if not frame.transcript_ids:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
await self._call_event_handler("on_transcript_drop", frame)
|
|
68
|
+
|
|
69
|
+
drop_ids = set(frame.transcript_ids)
|
|
70
|
+
if drop_ids:
|
|
71
|
+
self._processed_messages = [
|
|
72
|
+
msg for msg in self._processed_messages if msg.message_id not in drop_ids
|
|
73
|
+
]
|
|
74
|
+
|
|
60
75
|
|
|
61
76
|
class UserTranscriptProcessor(BaseTranscriptProcessor):
|
|
62
77
|
"""Processes user transcription frames into timestamped conversation messages."""
|
|
@@ -72,9 +87,15 @@ class UserTranscriptProcessor(BaseTranscriptProcessor):
|
|
|
72
87
|
|
|
73
88
|
if isinstance(frame, TranscriptionFrame):
|
|
74
89
|
message = TranscriptionMessage(
|
|
75
|
-
role="user",
|
|
90
|
+
role="user",
|
|
91
|
+
user_id=frame.user_id,
|
|
92
|
+
content=frame.text,
|
|
93
|
+
timestamp=frame.timestamp,
|
|
94
|
+
message_id=frame.id,
|
|
76
95
|
)
|
|
77
96
|
await self._emit_update([message])
|
|
97
|
+
elif isinstance(frame, TranscriptDropFrame):
|
|
98
|
+
await self._handle_transcript_drop(frame)
|
|
78
99
|
|
|
79
100
|
await self.push_frame(frame, direction)
|
|
80
101
|
|
pipecat/serializers/__init__.py
CHANGED
|
@@ -5,6 +5,7 @@ from .exotel import ExotelFrameSerializer
|
|
|
5
5
|
from .plivo import PlivoFrameSerializer
|
|
6
6
|
from .telnyx import TelnyxFrameSerializer
|
|
7
7
|
from .twilio import TwilioFrameSerializer
|
|
8
|
+
from .vi import VIFrameSerializer
|
|
8
9
|
|
|
9
10
|
__all__ = [
|
|
10
11
|
"FrameSerializer",
|
|
@@ -15,6 +16,7 @@ __all__ = [
|
|
|
15
16
|
"PlivoFrameSerializer",
|
|
16
17
|
"TelnyxFrameSerializer",
|
|
17
18
|
"TwilioFrameSerializer",
|
|
19
|
+
"VIFrameSerializer",
|
|
18
20
|
]
|
|
19
21
|
|
|
20
22
|
# Optional imports
|
pipecat/serializers/asterisk.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# asterisk_ws_serializer.py
|
|
2
|
+
"""Frame serializer for Asterisk WebSocket communication."""
|
|
3
|
+
|
|
2
4
|
import base64
|
|
3
5
|
import json
|
|
4
6
|
from typing import Literal, Optional
|
|
@@ -12,8 +14,8 @@ from pipecat.frames.frames import (
|
|
|
12
14
|
EndFrame,
|
|
13
15
|
Frame,
|
|
14
16
|
InputAudioRawFrame,
|
|
17
|
+
InterruptionFrame,
|
|
15
18
|
StartFrame,
|
|
16
|
-
StartInterruptionFrame,
|
|
17
19
|
TransportMessageFrame,
|
|
18
20
|
TransportMessageUrgentFrame,
|
|
19
21
|
)
|
|
@@ -21,6 +23,8 @@ from pipecat.serializers.base_serializer import FrameSerializer, FrameSerializer
|
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
class AsteriskFrameSerializer(FrameSerializer):
|
|
26
|
+
"""Serializes Pipecat frames to/from Asterisk WebSocket JSON messages."""
|
|
27
|
+
|
|
24
28
|
class InputParams(BaseModel):
|
|
25
29
|
"""Configuration parameters for AsteriskFrameSerializer.
|
|
26
30
|
|
|
@@ -39,6 +43,12 @@ class AsteriskFrameSerializer(FrameSerializer):
|
|
|
39
43
|
auto_hang_up: bool = False # no-op here; adapter handles hangup
|
|
40
44
|
|
|
41
45
|
def __init__(self, stream_id: str, params: Optional[InputParams] = None):
|
|
46
|
+
"""Initialize the Asterisk frame serializer.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
stream_id: Unique identifier for the media stream.
|
|
50
|
+
params: Configuration parameters for the serializer.
|
|
51
|
+
"""
|
|
42
52
|
self._stream_id = stream_id
|
|
43
53
|
self._params = params or AsteriskFrameSerializer.InputParams()
|
|
44
54
|
self._tel_rate = self._params.telephony_sample_rate
|
|
@@ -49,13 +59,16 @@ class AsteriskFrameSerializer(FrameSerializer):
|
|
|
49
59
|
|
|
50
60
|
@property
|
|
51
61
|
def type(self) -> FrameSerializerType:
|
|
62
|
+
"""Return the serializer type (TEXT for JSON messages)."""
|
|
52
63
|
return FrameSerializerType.TEXT # we send/recv JSON strings
|
|
53
64
|
|
|
54
65
|
async def setup(self, frame: StartFrame):
|
|
66
|
+
"""Setup the serializer with audio parameters from the StartFrame."""
|
|
55
67
|
self._sample_rate = self._params.sample_rate or frame.audio_in_sample_rate
|
|
56
68
|
|
|
57
69
|
# Pipecat -> Adapter (play to caller)
|
|
58
70
|
async def serialize(self, frame: Frame) -> str | bytes | None:
|
|
71
|
+
"""Serialize Pipecat frames to Asterisk WebSocket JSON messages."""
|
|
59
72
|
# On pipeline end, ask bridge to hang up
|
|
60
73
|
if (
|
|
61
74
|
self._params.auto_hang_up
|
|
@@ -64,7 +77,7 @@ class AsteriskFrameSerializer(FrameSerializer):
|
|
|
64
77
|
):
|
|
65
78
|
self._hangup_sent = True
|
|
66
79
|
return json.dumps({"event": "hangup"})
|
|
67
|
-
if isinstance(frame,
|
|
80
|
+
if isinstance(frame, InterruptionFrame):
|
|
68
81
|
return json.dumps({"event": "clear", "streamId": self._stream_id})
|
|
69
82
|
if isinstance(frame, AudioRawFrame):
|
|
70
83
|
pcm = frame.audio
|
|
@@ -114,6 +127,7 @@ class AsteriskFrameSerializer(FrameSerializer):
|
|
|
114
127
|
|
|
115
128
|
# Adapter -> Pipecat (audio from caller)
|
|
116
129
|
async def deserialize(self, data: str | bytes) -> Frame | None:
|
|
130
|
+
"""Deserialize Asterisk WebSocket JSON messages to Pipecat frames."""
|
|
117
131
|
try:
|
|
118
132
|
msg = json.loads(data)
|
|
119
133
|
except Exception:
|
pipecat/serializers/convox.py
CHANGED
|
@@ -22,9 +22,9 @@ from pipecat.frames.frames import (
|
|
|
22
22
|
Frame,
|
|
23
23
|
InputAudioRawFrame,
|
|
24
24
|
InputDTMFFrame,
|
|
25
|
+
InterruptionFrame,
|
|
25
26
|
KeypadEntry,
|
|
26
27
|
StartFrame,
|
|
27
|
-
StartInterruptionFrame,
|
|
28
28
|
TransportMessageFrame,
|
|
29
29
|
TransportMessageUrgentFrame,
|
|
30
30
|
)
|
|
@@ -117,7 +117,7 @@ class ConVoxFrameSerializer(FrameSerializer):
|
|
|
117
117
|
self._call_ended = True
|
|
118
118
|
# Return the callEnd event to be sent via the WebSocket
|
|
119
119
|
return await self._send_call_end_event()
|
|
120
|
-
elif isinstance(frame,
|
|
120
|
+
elif isinstance(frame, InterruptionFrame):
|
|
121
121
|
# Clear/interrupt command for ConVox
|
|
122
122
|
message = {
|
|
123
123
|
"event": "clear",
|
pipecat/serializers/custom.py
CHANGED
|
@@ -28,8 +28,8 @@ from pipecat.frames.frames import (
|
|
|
28
28
|
EndFrame,
|
|
29
29
|
Frame,
|
|
30
30
|
InputAudioRawFrame,
|
|
31
|
+
InterruptionFrame,
|
|
31
32
|
StartFrame,
|
|
32
|
-
StartInterruptionFrame,
|
|
33
33
|
TransportMessageFrame,
|
|
34
34
|
TransportMessageUrgentFrame,
|
|
35
35
|
)
|
|
@@ -121,7 +121,7 @@ class CustomFrameSerializer(FrameSerializer):
|
|
|
121
121
|
Returns:
|
|
122
122
|
Serialized data as JSON string, or None if the frame isn't handled.
|
|
123
123
|
"""
|
|
124
|
-
if isinstance(frame,
|
|
124
|
+
if isinstance(frame, InterruptionFrame):
|
|
125
125
|
# Send clear event to instruct client to discard buffered audio
|
|
126
126
|
answer = {"event": "clear", "stream_sid": self._stream_sid}
|
|
127
127
|
return json.dumps(answer)
|