dv-pipecat-ai 0.0.82.dev857__py3-none-any.whl → 0.0.85.dev837__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.

Files changed (195) hide show
  1. {dv_pipecat_ai-0.0.82.dev857.dist-info → dv_pipecat_ai-0.0.85.dev837.dist-info}/METADATA +98 -130
  2. {dv_pipecat_ai-0.0.82.dev857.dist-info → dv_pipecat_ai-0.0.85.dev837.dist-info}/RECORD +192 -140
  3. pipecat/adapters/base_llm_adapter.py +38 -1
  4. pipecat/adapters/services/anthropic_adapter.py +9 -14
  5. pipecat/adapters/services/aws_nova_sonic_adapter.py +120 -5
  6. pipecat/adapters/services/bedrock_adapter.py +236 -13
  7. pipecat/adapters/services/gemini_adapter.py +12 -8
  8. pipecat/adapters/services/open_ai_adapter.py +19 -7
  9. pipecat/adapters/services/open_ai_realtime_adapter.py +5 -0
  10. pipecat/audio/dtmf/dtmf-0.wav +0 -0
  11. pipecat/audio/dtmf/dtmf-1.wav +0 -0
  12. pipecat/audio/dtmf/dtmf-2.wav +0 -0
  13. pipecat/audio/dtmf/dtmf-3.wav +0 -0
  14. pipecat/audio/dtmf/dtmf-4.wav +0 -0
  15. pipecat/audio/dtmf/dtmf-5.wav +0 -0
  16. pipecat/audio/dtmf/dtmf-6.wav +0 -0
  17. pipecat/audio/dtmf/dtmf-7.wav +0 -0
  18. pipecat/audio/dtmf/dtmf-8.wav +0 -0
  19. pipecat/audio/dtmf/dtmf-9.wav +0 -0
  20. pipecat/audio/dtmf/dtmf-pound.wav +0 -0
  21. pipecat/audio/dtmf/dtmf-star.wav +0 -0
  22. pipecat/audio/filters/krisp_viva_filter.py +193 -0
  23. pipecat/audio/filters/noisereduce_filter.py +15 -0
  24. pipecat/audio/turn/base_turn_analyzer.py +9 -1
  25. pipecat/audio/turn/smart_turn/base_smart_turn.py +14 -8
  26. pipecat/audio/turn/smart_turn/data/__init__.py +0 -0
  27. pipecat/audio/turn/smart_turn/data/smart-turn-v3.0.onnx +0 -0
  28. pipecat/audio/turn/smart_turn/http_smart_turn.py +6 -2
  29. pipecat/audio/turn/smart_turn/local_smart_turn.py +1 -1
  30. pipecat/audio/turn/smart_turn/local_smart_turn_v2.py +1 -1
  31. pipecat/audio/turn/smart_turn/local_smart_turn_v3.py +124 -0
  32. pipecat/audio/vad/data/README.md +10 -0
  33. pipecat/audio/vad/data/silero_vad_v2.onnx +0 -0
  34. pipecat/audio/vad/silero.py +9 -3
  35. pipecat/audio/vad/vad_analyzer.py +13 -1
  36. pipecat/extensions/voicemail/voicemail_detector.py +5 -5
  37. pipecat/frames/frames.py +277 -86
  38. pipecat/observers/loggers/debug_log_observer.py +3 -3
  39. pipecat/observers/loggers/llm_log_observer.py +7 -3
  40. pipecat/observers/loggers/user_bot_latency_log_observer.py +22 -10
  41. pipecat/pipeline/runner.py +18 -6
  42. pipecat/pipeline/service_switcher.py +64 -36
  43. pipecat/pipeline/task.py +125 -79
  44. pipecat/pipeline/tts_switcher.py +30 -0
  45. pipecat/processors/aggregators/dtmf_aggregator.py +2 -3
  46. pipecat/processors/aggregators/{gated_openai_llm_context.py → gated_llm_context.py} +9 -9
  47. pipecat/processors/aggregators/gated_open_ai_llm_context.py +12 -0
  48. pipecat/processors/aggregators/llm_context.py +40 -2
  49. pipecat/processors/aggregators/llm_response.py +32 -15
  50. pipecat/processors/aggregators/llm_response_universal.py +19 -15
  51. pipecat/processors/aggregators/user_response.py +6 -6
  52. pipecat/processors/aggregators/vision_image_frame.py +24 -2
  53. pipecat/processors/audio/audio_buffer_processor.py +43 -8
  54. pipecat/processors/dtmf_aggregator.py +174 -77
  55. pipecat/processors/filters/stt_mute_filter.py +17 -0
  56. pipecat/processors/frame_processor.py +110 -24
  57. pipecat/processors/frameworks/langchain.py +8 -2
  58. pipecat/processors/frameworks/rtvi.py +210 -68
  59. pipecat/processors/frameworks/strands_agents.py +170 -0
  60. pipecat/processors/logger.py +2 -2
  61. pipecat/processors/transcript_processor.py +26 -5
  62. pipecat/processors/user_idle_processor.py +35 -11
  63. pipecat/runner/daily.py +59 -20
  64. pipecat/runner/run.py +395 -93
  65. pipecat/runner/types.py +6 -4
  66. pipecat/runner/utils.py +51 -10
  67. pipecat/serializers/__init__.py +5 -1
  68. pipecat/serializers/asterisk.py +16 -2
  69. pipecat/serializers/convox.py +41 -4
  70. pipecat/serializers/custom.py +257 -0
  71. pipecat/serializers/exotel.py +5 -5
  72. pipecat/serializers/livekit.py +20 -0
  73. pipecat/serializers/plivo.py +5 -5
  74. pipecat/serializers/protobuf.py +6 -5
  75. pipecat/serializers/telnyx.py +2 -2
  76. pipecat/serializers/twilio.py +43 -23
  77. pipecat/serializers/vi.py +324 -0
  78. pipecat/services/ai_service.py +2 -6
  79. pipecat/services/anthropic/llm.py +2 -25
  80. pipecat/services/assemblyai/models.py +6 -0
  81. pipecat/services/assemblyai/stt.py +13 -5
  82. pipecat/services/asyncai/tts.py +5 -3
  83. pipecat/services/aws/__init__.py +1 -0
  84. pipecat/services/aws/llm.py +147 -105
  85. pipecat/services/aws/nova_sonic/__init__.py +0 -0
  86. pipecat/services/aws/nova_sonic/context.py +436 -0
  87. pipecat/services/aws/nova_sonic/frames.py +25 -0
  88. pipecat/services/aws/nova_sonic/llm.py +1265 -0
  89. pipecat/services/aws/stt.py +3 -3
  90. pipecat/services/aws_nova_sonic/__init__.py +19 -1
  91. pipecat/services/aws_nova_sonic/aws.py +11 -1151
  92. pipecat/services/aws_nova_sonic/context.py +8 -354
  93. pipecat/services/aws_nova_sonic/frames.py +13 -17
  94. pipecat/services/azure/llm.py +51 -1
  95. pipecat/services/azure/realtime/__init__.py +0 -0
  96. pipecat/services/azure/realtime/llm.py +65 -0
  97. pipecat/services/azure/stt.py +15 -0
  98. pipecat/services/cartesia/stt.py +77 -70
  99. pipecat/services/cartesia/tts.py +80 -13
  100. pipecat/services/deepgram/__init__.py +1 -0
  101. pipecat/services/deepgram/flux/__init__.py +0 -0
  102. pipecat/services/deepgram/flux/stt.py +640 -0
  103. pipecat/services/elevenlabs/__init__.py +4 -1
  104. pipecat/services/elevenlabs/stt.py +339 -0
  105. pipecat/services/elevenlabs/tts.py +87 -46
  106. pipecat/services/fish/tts.py +5 -2
  107. pipecat/services/gemini_multimodal_live/events.py +38 -524
  108. pipecat/services/gemini_multimodal_live/file_api.py +23 -173
  109. pipecat/services/gemini_multimodal_live/gemini.py +41 -1403
  110. pipecat/services/gladia/stt.py +56 -72
  111. pipecat/services/google/__init__.py +1 -0
  112. pipecat/services/google/gemini_live/__init__.py +3 -0
  113. pipecat/services/google/gemini_live/file_api.py +189 -0
  114. pipecat/services/google/gemini_live/llm.py +1582 -0
  115. pipecat/services/google/gemini_live/llm_vertex.py +184 -0
  116. pipecat/services/google/llm.py +15 -11
  117. pipecat/services/google/llm_openai.py +3 -3
  118. pipecat/services/google/llm_vertex.py +86 -16
  119. pipecat/services/google/stt.py +4 -0
  120. pipecat/services/google/tts.py +7 -3
  121. pipecat/services/heygen/api.py +2 -0
  122. pipecat/services/heygen/client.py +8 -4
  123. pipecat/services/heygen/video.py +2 -0
  124. pipecat/services/hume/__init__.py +5 -0
  125. pipecat/services/hume/tts.py +220 -0
  126. pipecat/services/inworld/tts.py +6 -6
  127. pipecat/services/llm_service.py +15 -5
  128. pipecat/services/lmnt/tts.py +4 -2
  129. pipecat/services/mcp_service.py +4 -2
  130. pipecat/services/mem0/memory.py +6 -5
  131. pipecat/services/mistral/llm.py +29 -8
  132. pipecat/services/moondream/vision.py +42 -16
  133. pipecat/services/neuphonic/tts.py +5 -2
  134. pipecat/services/openai/__init__.py +1 -0
  135. pipecat/services/openai/base_llm.py +27 -20
  136. pipecat/services/openai/realtime/__init__.py +0 -0
  137. pipecat/services/openai/realtime/context.py +272 -0
  138. pipecat/services/openai/realtime/events.py +1106 -0
  139. pipecat/services/openai/realtime/frames.py +37 -0
  140. pipecat/services/openai/realtime/llm.py +829 -0
  141. pipecat/services/openai/tts.py +49 -10
  142. pipecat/services/openai_realtime/__init__.py +27 -0
  143. pipecat/services/openai_realtime/azure.py +21 -0
  144. pipecat/services/openai_realtime/context.py +21 -0
  145. pipecat/services/openai_realtime/events.py +21 -0
  146. pipecat/services/openai_realtime/frames.py +21 -0
  147. pipecat/services/openai_realtime_beta/azure.py +16 -0
  148. pipecat/services/openai_realtime_beta/openai.py +17 -5
  149. pipecat/services/piper/tts.py +7 -9
  150. pipecat/services/playht/tts.py +34 -4
  151. pipecat/services/rime/tts.py +12 -12
  152. pipecat/services/riva/stt.py +3 -1
  153. pipecat/services/salesforce/__init__.py +9 -0
  154. pipecat/services/salesforce/llm.py +700 -0
  155. pipecat/services/sarvam/__init__.py +7 -0
  156. pipecat/services/sarvam/stt.py +540 -0
  157. pipecat/services/sarvam/tts.py +97 -13
  158. pipecat/services/simli/video.py +2 -2
  159. pipecat/services/speechmatics/stt.py +22 -10
  160. pipecat/services/stt_service.py +47 -0
  161. pipecat/services/tavus/video.py +2 -2
  162. pipecat/services/tts_service.py +75 -22
  163. pipecat/services/vision_service.py +7 -6
  164. pipecat/services/vistaar/llm.py +51 -9
  165. pipecat/tests/utils.py +4 -4
  166. pipecat/transcriptions/language.py +41 -1
  167. pipecat/transports/base_input.py +13 -34
  168. pipecat/transports/base_output.py +140 -104
  169. pipecat/transports/daily/transport.py +199 -26
  170. pipecat/transports/heygen/__init__.py +0 -0
  171. pipecat/transports/heygen/transport.py +381 -0
  172. pipecat/transports/livekit/transport.py +228 -63
  173. pipecat/transports/local/audio.py +6 -1
  174. pipecat/transports/local/tk.py +11 -2
  175. pipecat/transports/network/fastapi_websocket.py +1 -1
  176. pipecat/transports/smallwebrtc/connection.py +103 -19
  177. pipecat/transports/smallwebrtc/request_handler.py +246 -0
  178. pipecat/transports/smallwebrtc/transport.py +65 -23
  179. pipecat/transports/tavus/transport.py +23 -12
  180. pipecat/transports/websocket/client.py +41 -5
  181. pipecat/transports/websocket/fastapi.py +21 -11
  182. pipecat/transports/websocket/server.py +14 -7
  183. pipecat/transports/whatsapp/api.py +8 -0
  184. pipecat/transports/whatsapp/client.py +47 -0
  185. pipecat/utils/base_object.py +54 -22
  186. pipecat/utils/redis.py +58 -0
  187. pipecat/utils/string.py +13 -1
  188. pipecat/utils/tracing/service_decorators.py +21 -21
  189. pipecat/serializers/genesys.py +0 -95
  190. pipecat/services/google/test-google-chirp.py +0 -45
  191. pipecat/services/openai.py +0 -698
  192. {dv_pipecat_ai-0.0.82.dev857.dist-info → dv_pipecat_ai-0.0.85.dev837.dist-info}/WHEEL +0 -0
  193. {dv_pipecat_ai-0.0.82.dev857.dist-info → dv_pipecat_ai-0.0.85.dev837.dist-info}/licenses/LICENSE +0 -0
  194. {dv_pipecat_ai-0.0.82.dev857.dist-info → dv_pipecat_ai-0.0.85.dev837.dist-info}/top_level.txt +0 -0
  195. /pipecat/services/{aws_nova_sonic → aws/nova_sonic}/ready.wav +0 -0
@@ -25,19 +25,20 @@ from pydantic import BaseModel
25
25
  from pipecat.audio.vad.vad_analyzer import VADAnalyzer, VADParams
26
26
  from pipecat.frames.frames import (
27
27
  CancelFrame,
28
+ ControlFrame,
28
29
  EndFrame,
29
30
  ErrorFrame,
30
31
  Frame,
31
32
  InputAudioRawFrame,
32
- InputTransportMessageUrgentFrame,
33
+ InputTransportMessageFrame,
33
34
  InterimTranscriptionFrame,
34
35
  OutputAudioRawFrame,
35
36
  OutputImageRawFrame,
37
+ OutputTransportMessageFrame,
38
+ OutputTransportMessageUrgentFrame,
36
39
  SpriteFrame,
37
40
  StartFrame,
38
41
  TranscriptionFrame,
39
- TransportMessageFrame,
40
- TransportMessageUrgentFrame,
41
42
  UserAudioRawFrame,
42
43
  UserImageRawFrame,
43
44
  UserImageRequestFrame,
@@ -61,9 +62,7 @@ try:
61
62
  VirtualCameraDevice,
62
63
  VirtualSpeakerDevice,
63
64
  )
64
- from daily import (
65
- LogLevel as DailyLogLevel,
66
- )
65
+ from daily import LogLevel as DailyLogLevel
67
66
  except ModuleNotFoundError as e:
68
67
  logger.error(f"Exception: {e}")
69
68
  logger.error(
@@ -75,7 +74,7 @@ VAD_RESET_PERIOD_MS = 2000
75
74
 
76
75
 
77
76
  @dataclass
78
- class DailyTransportMessageFrame(TransportMessageFrame):
77
+ class DailyOutputTransportMessageFrame(OutputTransportMessageFrame):
79
78
  """Frame for transport messages in Daily calls.
80
79
 
81
80
  Parameters:
@@ -86,7 +85,7 @@ class DailyTransportMessageFrame(TransportMessageFrame):
86
85
 
87
86
 
88
87
  @dataclass
89
- class DailyTransportMessageUrgentFrame(TransportMessageUrgentFrame):
88
+ class DailyOutputTransportMessageUrgentFrame(OutputTransportMessageUrgentFrame):
90
89
  """Frame for urgent transport messages in Daily calls.
91
90
 
92
91
  Parameters:
@@ -97,7 +96,59 @@ class DailyTransportMessageUrgentFrame(TransportMessageUrgentFrame):
97
96
 
98
97
 
99
98
  @dataclass
100
- class DailyInputTransportMessageUrgentFrame(InputTransportMessageUrgentFrame):
99
+ class DailyTransportMessageFrame(DailyOutputTransportMessageFrame):
100
+ """Frame for transport messages in Daily calls.
101
+
102
+ .. deprecated:: 0.0.87
103
+ This frame is deprecated and will be removed in a future version.
104
+ Instead, use `DailyOutputTransportMessageFrame`.
105
+
106
+ Parameters:
107
+ participant_id: Optional ID of the participant this message is for/from.
108
+ """
109
+
110
+ def __post_init__(self):
111
+ super().__post_init__()
112
+ import warnings
113
+
114
+ with warnings.catch_warnings():
115
+ warnings.simplefilter("always")
116
+ warnings.warn(
117
+ "DailyTransportMessageFrame is deprecated and will be removed in a future version. "
118
+ "Instead, use DailyOutputTransportMessageFrame.",
119
+ DeprecationWarning,
120
+ stacklevel=2,
121
+ )
122
+
123
+
124
+ @dataclass
125
+ class DailyTransportMessageUrgentFrame(DailyOutputTransportMessageUrgentFrame):
126
+ """Frame for urgent transport messages in Daily calls.
127
+
128
+ .. deprecated:: 0.0.87
129
+ This frame is deprecated and will be removed in a future version.
130
+ Instead, use `DailyOutputTransportMessageUrgentFrame`.
131
+
132
+ Parameters:
133
+ participant_id: Optional ID of the participant this message is for/from.
134
+ """
135
+
136
+ def __post_init__(self):
137
+ super().__post_init__()
138
+ import warnings
139
+
140
+ with warnings.catch_warnings():
141
+ warnings.simplefilter("always")
142
+ warnings.warn(
143
+ "DailyTransportMessageUrgentFrame is deprecated and will be removed in a future version. "
144
+ "Instead, use DailyOutputTransportMessageUrgentFrame.",
145
+ DeprecationWarning,
146
+ stacklevel=2,
147
+ )
148
+
149
+
150
+ @dataclass
151
+ class DailyInputTransportMessageFrame(InputTransportMessageFrame):
101
152
  """Frame for input urgent transport messages in Daily calls.
102
153
 
103
154
  Parameters:
@@ -107,6 +158,62 @@ class DailyInputTransportMessageUrgentFrame(InputTransportMessageUrgentFrame):
107
158
  participant_id: Optional[str] = None
108
159
 
109
160
 
161
+ class DailyInputTransportMessageUrgentFrame(DailyInputTransportMessageFrame):
162
+ """Frame for input urgent transport messages in Daily calls.
163
+
164
+ .. deprecated:: 0.0.87
165
+ This frame is deprecated and will be removed in a future version.
166
+ Instead, use `DailyInputTransportMessageFrame`.
167
+
168
+ Parameters:
169
+ participant_id: Optional ID of the participant this message is for/from.
170
+ """
171
+
172
+ def __post_init__(self):
173
+ super().__post_init__()
174
+ import warnings
175
+
176
+ with warnings.catch_warnings():
177
+ warnings.simplefilter("always")
178
+ warnings.warn(
179
+ "DailyInputTransportMessageUrgentFrame is deprecated and will be removed in a future version. "
180
+ "Instead, use DailyInputTransportMessageFrame.",
181
+ DeprecationWarning,
182
+ stacklevel=2,
183
+ )
184
+
185
+
186
+ @dataclass
187
+ class DailyUpdateRemoteParticipantsFrame(ControlFrame):
188
+ """Frame to update remote participants in Daily calls.
189
+
190
+ .. deprecated:: 0.0.87
191
+ `DailyUpdateRemoteParticipantsFrame` is deprecated and will be removed in a future version.
192
+ Create your own custom frame and use a custom processor to handle it or use, for example,
193
+ `on_after_push_frame` event instead in the output transport.
194
+
195
+ Parameters:
196
+ remote_participants: See https://reference-python.daily.co/api_reference.html#daily.CallClient.update_remote_participants.
197
+ """
198
+
199
+ remote_participants: Mapping[str, Any] = None
200
+
201
+ def __post_init__(self):
202
+ super().__post_init__()
203
+ import warnings
204
+
205
+ with warnings.catch_warnings():
206
+ warnings.simplefilter("always")
207
+ warnings.warn(
208
+ "DailyUpdateRemoteParticipantsFrame is deprecated and will be removed in a future version."
209
+ "Instead, create your own custom frame and handle it in the "
210
+ '`@transport.output().event_handler("on_after_push_frame")` event handler or a '
211
+ "custom processor.",
212
+ DeprecationWarning,
213
+ stacklevel=2,
214
+ )
215
+
216
+
110
217
  class WebRTCVADAnalyzer(VADAnalyzer):
111
218
  """Voice Activity Detection analyzer using WebRTC.
112
219
 
@@ -217,6 +324,7 @@ class DailyCallbacks(BaseModel):
217
324
  on_active_speaker_changed: Called when the active speaker of the call has changed.
218
325
  on_joined: Called when bot successfully joined a room.
219
326
  on_left: Called when bot left a room.
327
+ on_before_leave: Called when bot is about to leave the room.
220
328
  on_error: Called when an error occurs.
221
329
  on_app_message: Called when receiving an app message.
222
330
  on_call_state_updated: Called when call state changes.
@@ -246,6 +354,7 @@ class DailyCallbacks(BaseModel):
246
354
  on_active_speaker_changed: Callable[[Mapping[str, Any]], Awaitable[None]]
247
355
  on_joined: Callable[[Mapping[str, Any]], Awaitable[None]]
248
356
  on_left: Callable[[], Awaitable[None]]
357
+ on_before_leave: Callable[[], Awaitable[None]]
249
358
  on_error: Callable[[str], Awaitable[None]]
250
359
  on_app_message: Callable[[Any, str], Awaitable[None]]
251
360
  on_call_state_updated: Callable[[str], Awaitable[None]]
@@ -361,6 +470,7 @@ class DailyTransportClient(EventHandler):
361
470
  self._transcription_ids = []
362
471
  self._transcription_status = None
363
472
  self._dial_out_session_id: str = ""
473
+ self._dial_in_session_id: str = ""
364
474
 
365
475
  self._joining = False
366
476
  self._joined = False
@@ -441,7 +551,9 @@ class DailyTransportClient(EventHandler):
441
551
  """
442
552
  return self._out_sample_rate
443
553
 
444
- async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame):
554
+ async def send_message(
555
+ self, frame: OutputTransportMessageFrame | OutputTransportMessageUrgentFrame
556
+ ):
445
557
  """Send an application message to participants.
446
558
 
447
559
  Args:
@@ -451,7 +563,9 @@ class DailyTransportClient(EventHandler):
451
563
  return
452
564
 
453
565
  participant_id = None
454
- if isinstance(frame, (DailyTransportMessageFrame, DailyTransportMessageUrgentFrame)):
566
+ if isinstance(
567
+ frame, (DailyOutputTransportMessageFrame, DailyOutputTransportMessageUrgentFrame)
568
+ ):
455
569
  participant_id = frame.participant_id
456
570
 
457
571
  future = self._get_event_loop().create_future()
@@ -493,11 +607,14 @@ class DailyTransportClient(EventHandler):
493
607
  self._custom_audio_tracks[destination] = await self.add_custom_audio_track(destination)
494
608
  self._client.update_publishing({"customAudio": {destination: True}})
495
609
 
496
- async def write_audio_frame(self, frame: OutputAudioRawFrame):
610
+ async def write_audio_frame(self, frame: OutputAudioRawFrame) -> bool:
497
611
  """Write an audio frame to the appropriate audio track.
498
612
 
499
613
  Args:
500
614
  frame: The audio frame to write.
615
+
616
+ Returns:
617
+ True if the audio frame was written successfully, False otherwise.
501
618
  """
502
619
  future = self._get_event_loop().create_future()
503
620
 
@@ -513,18 +630,24 @@ class DailyTransportClient(EventHandler):
513
630
  audio_source.write_frames(frame.audio, completion=completion_callback(future))
514
631
  else:
515
632
  logger.warning(f"{self} unable to write audio frames to destination [{destination}]")
516
- future.set_result(None)
633
+ future.set_result(0)
517
634
 
518
- await future
635
+ num_frames = await future
636
+ return num_frames > 0
519
637
 
520
- async def write_video_frame(self, frame: OutputImageRawFrame):
638
+ async def write_video_frame(self, frame: OutputImageRawFrame) -> bool:
521
639
  """Write a video frame to the camera device.
522
640
 
523
641
  Args:
524
642
  frame: The image frame to write.
643
+
644
+ Returns:
645
+ True if the video frame was written successfully, False otherwise.
525
646
  """
526
647
  if not frame.transport_destination and self._camera:
527
648
  self._camera.write_frame(frame.image)
649
+ return True
650
+ return False
528
651
 
529
652
  async def setup(self, setup: FrameProcessorSetup):
530
653
  """Setup the client with task manager and event queues.
@@ -721,6 +844,9 @@ class DailyTransportClient(EventHandler):
721
844
 
722
845
  logger.info(f"Leaving {self._room_url}")
723
846
 
847
+ # Call callback before leaving.
848
+ await self._callbacks.on_before_leave()
849
+
724
850
  if self._params.transcription_enabled:
725
851
  await self.stop_transcription()
726
852
 
@@ -825,6 +951,16 @@ class DailyTransportClient(EventHandler):
825
951
  Args:
826
952
  settings: SIP call transfer settings.
827
953
  """
954
+ session_id = (
955
+ settings.get("sessionId") or self._dial_out_session_id or self._dial_in_session_id
956
+ )
957
+ if not session_id:
958
+ logger.error("Unable to transfer SIP call: 'sessionId' is not set")
959
+ return
960
+
961
+ # Update 'sessionId' field.
962
+ settings["sessionId"] = session_id
963
+
828
964
  future = self._get_event_loop().create_future()
829
965
  self._client.sip_call_transfer(settings, completion=completion_callback(future))
830
966
  await future
@@ -1143,6 +1279,7 @@ class DailyTransportClient(EventHandler):
1143
1279
  Args:
1144
1280
  data: Dial-in connection data.
1145
1281
  """
1282
+ self._dial_in_session_id = data["sessionId"] if "sessionId" in data else ""
1146
1283
  self._call_event_callback(self._callbacks.on_dialin_connected, data)
1147
1284
 
1148
1285
  def on_dialin_ready(self, sip_endpoint: str):
@@ -1159,6 +1296,9 @@ class DailyTransportClient(EventHandler):
1159
1296
  Args:
1160
1297
  data: Dial-in stop data.
1161
1298
  """
1299
+ # Cleanup only if our session stopped.
1300
+ if data.get("sessionId") == self._dial_in_session_id:
1301
+ self._dial_in_session_id = ""
1162
1302
  self._call_event_callback(self._callbacks.on_dialin_stopped, data)
1163
1303
 
1164
1304
  def on_dialin_error(self, data: Any):
@@ -1167,6 +1307,9 @@ class DailyTransportClient(EventHandler):
1167
1307
  Args:
1168
1308
  data: Dial-in error data.
1169
1309
  """
1310
+ # Cleanup only if our session errored out.
1311
+ if data.get("sessionId") == self._dial_in_session_id:
1312
+ self._dial_in_session_id = ""
1170
1313
  self._call_event_callback(self._callbacks.on_dialin_error, data)
1171
1314
 
1172
1315
  def on_dialin_warning(self, data: Any):
@@ -1201,7 +1344,7 @@ class DailyTransportClient(EventHandler):
1201
1344
  data: Dial-out stop data.
1202
1345
  """
1203
1346
  # Cleanup only if our session stopped.
1204
- if data["sessionId"] == self._dial_out_session_id:
1347
+ if data.get("sessionId") == self._dial_out_session_id:
1205
1348
  self._dial_out_session_id = ""
1206
1349
  self._call_event_callback(self._callbacks.on_dialout_stopped, data)
1207
1350
 
@@ -1212,7 +1355,7 @@ class DailyTransportClient(EventHandler):
1212
1355
  data: Dial-out error data.
1213
1356
  """
1214
1357
  # Cleanup only if our session errored out.
1215
- if data["sessionId"] == self._dial_out_session_id:
1358
+ if data.get("sessionId") == self._dial_out_session_id:
1216
1359
  self._dial_out_session_id = ""
1217
1360
  self._call_event_callback(self._callbacks.on_dialout_error, data)
1218
1361
 
@@ -1559,7 +1702,7 @@ class DailyInputTransport(BaseInputTransport):
1559
1702
  message: The message data to send.
1560
1703
  sender: ID of the message sender.
1561
1704
  """
1562
- frame = DailyInputTransportMessageUrgentFrame(message=message, participant_id=sender)
1705
+ frame = DailyInputTransportMessageFrame(message=message, participant_id=sender)
1563
1706
  await self.push_frame(frame)
1564
1707
 
1565
1708
  #
@@ -1769,7 +1912,21 @@ class DailyOutputTransport(BaseOutputTransport):
1769
1912
  # Leave the room.
1770
1913
  await self._client.leave()
1771
1914
 
1772
- async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame):
1915
+ async def process_frame(self, frame: Frame, direction: FrameDirection):
1916
+ """Process outgoing frames, including transport messages.
1917
+
1918
+ Args:
1919
+ frame: The frame to process.
1920
+ direction: The direction of frame flow in the pipeline.
1921
+ """
1922
+ await super().process_frame(frame, direction)
1923
+
1924
+ if isinstance(frame, DailyUpdateRemoteParticipantsFrame):
1925
+ await self._client.update_remote_participants(frame.remote_participants)
1926
+
1927
+ async def send_message(
1928
+ self, frame: OutputTransportMessageFrame | OutputTransportMessageUrgentFrame
1929
+ ):
1773
1930
  """Send a transport message to participants.
1774
1931
 
1775
1932
  Args:
@@ -1790,24 +1947,33 @@ class DailyOutputTransport(BaseOutputTransport):
1790
1947
 
1791
1948
  Args:
1792
1949
  destination: The destination identifier to register.
1950
+
1951
+ Returns:
1952
+ True if the audio frame was written successfully, False otherwise.
1793
1953
  """
1794
1954
  await self._client.register_audio_destination(destination)
1795
1955
 
1796
- async def write_audio_frame(self, frame: OutputAudioRawFrame):
1956
+ async def write_audio_frame(self, frame: OutputAudioRawFrame) -> bool:
1797
1957
  """Write an audio frame to the Daily call.
1798
1958
 
1799
1959
  Args:
1800
1960
  frame: The audio frame to write.
1961
+
1962
+ Returns:
1963
+ True if the audio frame was written successfully, False otherwise.
1801
1964
  """
1802
- await self._client.write_audio_frame(frame)
1965
+ return await self._client.write_audio_frame(frame)
1803
1966
 
1804
- async def write_video_frame(self, frame: OutputImageRawFrame):
1967
+ async def write_video_frame(self, frame: OutputImageRawFrame) -> bool:
1805
1968
  """Write a video frame to the Daily call.
1806
1969
 
1807
1970
  Args:
1808
1971
  frame: The video frame to write.
1972
+
1973
+ Returns:
1974
+ True if the video frame was written successfully, False otherwise.
1809
1975
  """
1810
- await self._client.write_video_frame(frame)
1976
+ return await self._client.write_video_frame(frame)
1811
1977
 
1812
1978
  def _supports_native_dtmf(self) -> bool:
1813
1979
  """Daily supports native DTMF via telephone events.
@@ -1864,6 +2030,7 @@ class DailyTransport(BaseTransport):
1864
2030
  on_active_speaker_changed=self._on_active_speaker_changed,
1865
2031
  on_joined=self._on_joined,
1866
2032
  on_left=self._on_left,
2033
+ on_before_leave=self._on_before_leave,
1867
2034
  on_error=self._on_error,
1868
2035
  on_app_message=self._on_app_message,
1869
2036
  on_call_state_updated=self._on_call_state_updated,
@@ -1927,6 +2094,7 @@ class DailyTransport(BaseTransport):
1927
2094
  self._register_event_handler("on_recording_started")
1928
2095
  self._register_event_handler("on_recording_stopped")
1929
2096
  self._register_event_handler("on_recording_error")
2097
+ self._register_event_handler("on_before_leave", sync=True)
1930
2098
 
1931
2099
  #
1932
2100
  # BaseTransport
@@ -2178,6 +2346,10 @@ class DailyTransport(BaseTransport):
2178
2346
  """Handle room left events."""
2179
2347
  await self._call_event_handler("on_left")
2180
2348
 
2349
+ async def _on_before_leave(self):
2350
+ """Handle before leave room events."""
2351
+ await self._call_event_handler("on_before_leave")
2352
+
2181
2353
  async def _on_error(self, error):
2182
2354
  """Handle error events and push error frames."""
2183
2355
  await self._call_event_handler("on_error", error)
@@ -2317,7 +2489,7 @@ class DailyTransport(BaseTransport):
2317
2489
  """Handle participant updated events."""
2318
2490
  await self._call_event_handler("on_participant_updated", participant)
2319
2491
 
2320
- async def _on_transcription_message(self, message):
2492
+ async def _on_transcription_message(self, message: Mapping[str, Any]) -> None:
2321
2493
  """Handle transcription message events."""
2322
2494
  await self._call_event_handler("on_transcription_message", message)
2323
2495
 
@@ -2329,9 +2501,10 @@ class DailyTransport(BaseTransport):
2329
2501
 
2330
2502
  text = message["text"]
2331
2503
  timestamp = message["timestamp"]
2332
- is_final = message["rawResponse"]["is_final"]
2504
+ raw_response = message.get("rawResponse", {})
2505
+ is_final = raw_response.get("is_final", False)
2333
2506
  try:
2334
- language = message["rawResponse"]["channel"]["alternatives"][0]["languages"][0]
2507
+ language = raw_response["channel"]["alternatives"][0]["languages"][0]
2335
2508
  language = Language(language)
2336
2509
  except KeyError:
2337
2510
  language = None
File without changes