dv-pipecat-ai 0.0.85.dev821__py3-none-any.whl → 0.0.85.dev823__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dv-pipecat-ai
3
- Version: 0.0.85.dev821
3
+ Version: 0.0.85.dev823
4
4
  Summary: An open source framework for voice (and multimodal) assistants
5
5
  License-Expression: BSD-2-Clause
6
6
  Project-URL: Source, https://github.com/pipecat-ai/pipecat
@@ -1,4 +1,4 @@
1
- dv_pipecat_ai-0.0.85.dev821.dist-info/licenses/LICENSE,sha256=DWY2QGf2eMCFhuu2ChairtT6CB7BEFffNVhXWc4Od08,1301
1
+ dv_pipecat_ai-0.0.85.dev823.dist-info/licenses/LICENSE,sha256=DWY2QGf2eMCFhuu2ChairtT6CB7BEFffNVhXWc4Od08,1301
2
2
  pipecat/__init__.py,sha256=j0Xm6adxHhd7D06dIyyPV_GlBYLlBnTAERVvD_jAARQ,861
3
3
  pipecat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  pipecat/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -79,7 +79,7 @@ pipecat/extensions/voicemail/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
79
79
  pipecat/extensions/voicemail/voicemail_detector.py,sha256=JxmU2752iWP_1_GmzZReNESUTFAeyEa4XBPL20_C208,30004
80
80
  pipecat/frames/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
81
  pipecat/frames/frames.proto,sha256=JXZm3VXLR8zMOUcOuhVoe2mhM3MQIQGMJXLopdJO_5Y,839
82
- pipecat/frames/frames.py,sha256=_GbvjOe1HRDSVCTqF5nvRaA-oCFrtyWfl457Uq0qkGw,49229
82
+ pipecat/frames/frames.py,sha256=CxlrFst5DuD6kDp2CE6kWigVezF94y-Snf6h8w1pwVU,49522
83
83
  pipecat/frames/protobufs/frames_pb2.py,sha256=VHgGV_W7qQ4sfQK6RHb5_DggLm3PiSYMr6aBZ8_p1cQ,2590
84
84
  pipecat/metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
85
  pipecat/metrics/metrics.py,sha256=bdZNciEtLTtA-xgoKDz2RJAy6fKrXkTwz3pryVHzc2M,2713
@@ -108,12 +108,12 @@ pipecat/processors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
108
108
  pipecat/processors/async_generator.py,sha256=qPOZxk5eOad_NrF_Z06vWZ6deXIxb9AKZKYO2e5pkJs,2385
109
109
  pipecat/processors/consumer_processor.py,sha256=DrWCKnfblknZJ0bLmR_unIeJ1axQw4IPUn2IB3KLGGA,3228
110
110
  pipecat/processors/dtmf_aggregator.py,sha256=mo_IXUlsnVl-_Xn8sbTGnRF4Lkts0h6E3uauGbeFyWs,10204
111
- pipecat/processors/frame_processor.py,sha256=Qf-EJCWlw2itvJTsFykKBfjcsXRQUDgSqJDF8gb60V0,33806
111
+ pipecat/processors/frame_processor.py,sha256=uBu6Waa0_diMXdQXMZ5V5a_KwaaPzcieyuv5gO9u-ME,33841
112
112
  pipecat/processors/idle_frame_processor.py,sha256=z8AuhGap61lA5K35P6XCaOpn4kkmK_9NZNppbpQxheU,3124
113
113
  pipecat/processors/logger.py,sha256=8xa4KKekXQIETlQR7zoGnwUpLNo8CeDVm7YjyXePN-w,2385
114
114
  pipecat/processors/producer_processor.py,sha256=iIIOHZd77APvUGP7JqFbznAHUnCULcq_qYiSEjwXHcc,3265
115
115
  pipecat/processors/text_transformer.py,sha256=LnfWJYzntJhZhrQ1lgSSY4D4VbHtrQJgrC227M69ZYU,1718
116
- pipecat/processors/transcript_processor.py,sha256=9F00tY3cxt63ZhYvFGSSAnuUTt3J16mEOUHMzIMndMY,11720
116
+ pipecat/processors/transcript_processor.py,sha256=fr5JtlTOfmKnfmYG8ZwRj4DpZWP-uuGi6aNNKtlLxRg,12491
117
117
  pipecat/processors/two_stage_user_idle_processor.py,sha256=uf2aZh_lfW-eMxmFogP3R4taAJ1yXOSqjKsR7oXtD0Y,2938
118
118
  pipecat/processors/user_idle_processor.py,sha256=Dl-Kcg0B4JZqWXXiyGuvYszGimbu2oKOyOJC92R9_hE,9140
119
119
  pipecat/processors/aggregators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -122,7 +122,7 @@ pipecat/processors/aggregators/gated.py,sha256=tii0sRrBkRW6y9Xq5iTWPnqlOEejU4VqP
122
122
  pipecat/processors/aggregators/gated_llm_context.py,sha256=CPv6sMA8irD1zZ3fU1gSv6D7qcPvCA0MdpFhBtJ_ekI,3007
123
123
  pipecat/processors/aggregators/gated_open_ai_llm_context.py,sha256=DgqmdPj1u3fP_SVmxtfP7NjHqnyhN_RVVTDfmjbkxAs,361
124
124
  pipecat/processors/aggregators/llm_context.py,sha256=wNbZA0Vt0FzNc5cu06xiv1z7DIClIlfqR1ZD8EusbVw,11085
125
- pipecat/processors/aggregators/llm_response.py,sha256=igjIcBwzXzULWQIzM6XxXlXCHbR4Q5tAHP8PBHaPVNQ,47314
125
+ pipecat/processors/aggregators/llm_response.py,sha256=cBNGU8Ld4zT36-QsE1EJemrNA12q7lc9i-vLM9qmLcQ,48075
126
126
  pipecat/processors/aggregators/llm_response_universal.py,sha256=5PqmpATpekD8BVWyBExZgatKHsNbZem8M-A7_VwTbiQ,34334
127
127
  pipecat/processors/aggregators/openai_llm_context.py,sha256=cC8DXdVPERRN04i0i-1Ys6kusvnbMALeH-Z8Pu5K684,12999
128
128
  pipecat/processors/aggregators/sentence.py,sha256=E7e3knfQl6HEGpYMKPklF1aO_gOn-rr7SnynErwfkQk,2235
@@ -415,7 +415,7 @@ pipecat/utils/tracing/service_decorators.py,sha256=fwzxFpi8DJl6BJbK74G0UEB4ccMJg
415
415
  pipecat/utils/tracing/setup.py,sha256=7TEgPNpq6M8lww8OQvf0P9FzYc5A30xICGklVA-fua0,2892
416
416
  pipecat/utils/tracing/turn_context_provider.py,sha256=ikon3plFOx0XbMrH6DdeHttNpb-U0gzMZIm3bWLc9eI,2485
417
417
  pipecat/utils/tracing/turn_trace_observer.py,sha256=dma16SBJpYSOE58YDWy89QzHyQFc_9gQZszKeWixuwc,9725
418
- dv_pipecat_ai-0.0.85.dev821.dist-info/METADATA,sha256=mkEkQu0dIz-W8gqGGXsrqlfcD_AzqceP8-vCK0UgMfY,32924
419
- dv_pipecat_ai-0.0.85.dev821.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
420
- dv_pipecat_ai-0.0.85.dev821.dist-info/top_level.txt,sha256=kQzG20CxGf-nSsHmtXHx3hY2-8zHA3jYg8jk0TajqXc,8
421
- dv_pipecat_ai-0.0.85.dev821.dist-info/RECORD,,
418
+ dv_pipecat_ai-0.0.85.dev823.dist-info/METADATA,sha256=QzFZChlDc4joKwMeDE3JSyM7EQzpJLPuIyfsNhrVgzE,32924
419
+ dv_pipecat_ai-0.0.85.dev823.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
420
+ dv_pipecat_ai-0.0.85.dev823.dist-info/top_level.txt,sha256=kQzG20CxGf-nSsHmtXHx3hY2-8zHA3jYg8jk0TajqXc,8
421
+ dv_pipecat_ai-0.0.85.dev823.dist-info/RECORD,,
pipecat/frames/frames.py CHANGED
@@ -457,6 +457,7 @@ class TranscriptionMessage:
457
457
  content: str
458
458
  user_id: Optional[str] = None
459
459
  timestamp: Optional[str] = None
460
+ message_id: Optional[int] = None
460
461
 
461
462
 
462
463
  @dataclass
@@ -510,6 +511,17 @@ class TranscriptionUpdateFrame(DataFrame):
510
511
  return f"{self.name}(pts: {pts}, messages: {len(self.messages)})"
511
512
 
512
513
 
514
+ @dataclass
515
+ class TranscriptDropFrame(DataFrame):
516
+ """Frame indicating previously emitted transcript chunks should be discarded.
517
+
518
+ Parameters:
519
+ transcript_ids: List of frame/message identifiers to drop.
520
+ """
521
+
522
+ transcript_ids: List[int]
523
+
524
+
513
525
  @dataclass
514
526
  class LLMContextFrame(Frame):
515
527
  """Frame containing a universal LLM context.
@@ -48,9 +48,10 @@ from pipecat.frames.frames import (
48
48
  LLMTextFrame,
49
49
  OpenAILLMContextAssistantTimestampFrame,
50
50
  SpeechControlParamsFrame,
51
- StartInterruptionFrame,
52
51
  StartFrame,
52
+ StartInterruptionFrame,
53
53
  TextFrame,
54
+ TranscriptDropFrame,
54
55
  TranscriptionFrame,
55
56
  UserImageRawFrame,
56
57
  UserStartedSpeakingFrame,
@@ -446,6 +447,7 @@ class LLMUserContextAggregator(LLMContextResponseAggregator):
446
447
  self._latest_final_transcript = ""
447
448
  self._last_user_speaking_time = 0
448
449
  self._last_aggregation_push_time = 0
450
+ self._pending_transcription_ids: List[int] = []
449
451
 
450
452
  async def reset(self):
451
453
  """Reset the aggregation state and interruption strategies."""
@@ -453,6 +455,7 @@ class LLMUserContextAggregator(LLMContextResponseAggregator):
453
455
  self._was_bot_speaking = False
454
456
  self._seen_interim_results = False
455
457
  self._waiting_for_aggregation = False
458
+ self._pending_transcription_ids.clear()
456
459
  [await s.reset() for s in self._interruption_strategies]
457
460
 
458
461
  async def handle_aggregation(self, aggregation: str):
@@ -588,6 +591,17 @@ class LLMUserContextAggregator(LLMContextResponseAggregator):
588
591
 
589
592
  return any([await should_interrupt(s) for s in self._interruption_strategies])
590
593
 
594
+ async def _discard_pending_transcriptions(self, reason: str):
595
+ """Notify upstream processors that pending transcripts should be dropped."""
596
+ if self._pending_transcription_ids:
597
+ drop_frame = TranscriptDropFrame(transcript_ids=list(self._pending_transcription_ids))
598
+ self.logger.debug(
599
+ f"Dropping {len(self._pending_transcription_ids)} transcript chunk(s) due to {reason}"
600
+ )
601
+ await self.push_frame(drop_frame, FrameDirection.UPSTREAM)
602
+ self._pending_transcription_ids.clear()
603
+ self._aggregation = ""
604
+
591
605
  async def _start(self, frame: StartFrame):
592
606
  self._create_aggregation_task()
593
607
 
@@ -616,8 +630,7 @@ class LLMUserContextAggregator(LLMContextResponseAggregator):
616
630
 
617
631
  async def _handle_user_started_speaking(self, frame: UserStartedSpeakingFrame):
618
632
  if len(self._aggregation) > 0:
619
- self.logger.debug(f"Dropping {self._aggregation}")
620
- self._aggregation = ""
633
+ await self._discard_pending_transcriptions("user_started_speaking")
621
634
  self._latest_final_transcript = ""
622
635
  self._last_user_speaking_time = time.time()
623
636
  self._user_speaking = True
@@ -662,6 +675,7 @@ class LLMUserContextAggregator(LLMContextResponseAggregator):
662
675
  return
663
676
 
664
677
  self._aggregation += f" {text}" if self._aggregation else text
678
+ self._pending_transcription_ids.append(frame.id)
665
679
  # We just got a final result, so let's reset interim results.
666
680
  self._seen_interim_results = False
667
681
 
@@ -591,7 +591,7 @@ class FrameProcessor(BaseObject):
591
591
 
592
592
  async def pause_processing_system_frames(self):
593
593
  """Pause processing of queued system frames."""
594
- logger.trace(f"{self}: pausing system frame processing")
594
+ self.logger.trace(f"{self}: pausing system frame processing")
595
595
  self.__should_block_system_frames = True
596
596
  if self.__input_event:
597
597
  self.__input_event.clear()
@@ -812,7 +812,7 @@ class FrameProcessor(BaseObject):
812
812
  True if the processor has been started.
813
813
  """
814
814
  if not self.__started:
815
- logger.error(f"{self} Trying to process {frame} but StartFrame not received yet")
815
+ self.logger.error(f"{self} Trying to process {frame} but StartFrame not received yet")
816
816
  return self.__started
817
817
 
818
818
  def __create_input_task(self):
@@ -876,7 +876,7 @@ class FrameProcessor(BaseObject):
876
876
 
877
877
  await self._call_event_handler("on_after_process_frame", frame)
878
878
  except Exception as e:
879
- logger.exception(f"{self}: error processing frame: {e}")
879
+ self.logger.exception(f"{self}: error processing frame: {e}")
880
880
  await self.push_error(ErrorFrame(str(e)))
881
881
 
882
882
  async def __input_frame_task_handler(self):
@@ -890,11 +890,11 @@ class FrameProcessor(BaseObject):
890
890
  (frame, direction, callback) = await self.__input_queue.get()
891
891
 
892
892
  if self.__should_block_system_frames and self.__input_event:
893
- logger.trace(f"{self}: system frame processing paused")
893
+ self.logger.trace(f"{self}: system frame processing paused")
894
894
  await self.__input_event.wait()
895
895
  self.__input_event.clear()
896
896
  self.__should_block_system_frames = False
897
- logger.trace(f"{self}: system frame processing resumed")
897
+ self.logger.trace(f"{self}: system frame processing resumed")
898
898
 
899
899
  if isinstance(frame, SystemFrame):
900
900
  await self.__process_frame(frame, direction, callback)
@@ -913,11 +913,11 @@ class FrameProcessor(BaseObject):
913
913
  (frame, direction, callback) = await self.__process_queue.get()
914
914
 
915
915
  if self.__should_block_frames and self.__process_event:
916
- logger.trace(f"{self}: frame processing paused")
916
+ self.logger.trace(f"{self}: frame processing paused")
917
917
  await self.__process_event.wait()
918
918
  self.__process_event.clear()
919
919
  self.__should_block_frames = False
920
- logger.trace(f"{self}: frame processing resumed")
920
+ self.logger.trace(f"{self}: frame processing resumed")
921
921
 
922
922
  await self.__process_frame(frame, direction, callback)
923
923
 
@@ -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,18 @@ 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
+ drop_ids = set(frame.transcript_ids)
68
+ if drop_ids:
69
+ self._processed_messages = [
70
+ msg for msg in self._processed_messages if msg.message_id not in drop_ids
71
+ ]
72
+ await self._call_event_handler("on_transcript_drop", frame)
73
+
60
74
 
61
75
  class UserTranscriptProcessor(BaseTranscriptProcessor):
62
76
  """Processes user transcription frames into timestamped conversation messages."""
@@ -72,9 +86,15 @@ class UserTranscriptProcessor(BaseTranscriptProcessor):
72
86
 
73
87
  if isinstance(frame, TranscriptionFrame):
74
88
  message = TranscriptionMessage(
75
- role="user", user_id=frame.user_id, content=frame.text, timestamp=frame.timestamp
89
+ role="user",
90
+ user_id=frame.user_id,
91
+ content=frame.text,
92
+ timestamp=frame.timestamp,
93
+ message_id=frame.id,
76
94
  )
77
95
  await self._emit_update([message])
96
+ elif isinstance(frame, TranscriptDropFrame):
97
+ await self._handle_transcript_drop(frame)
78
98
 
79
99
  await self.push_frame(frame, direction)
80
100