dv-pipecat-ai 0.0.82.dev815__py3-none-any.whl → 0.0.82.dev857__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.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/METADATA +8 -3
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/RECORD +106 -79
- pipecat/adapters/base_llm_adapter.py +44 -6
- pipecat/adapters/services/anthropic_adapter.py +302 -2
- pipecat/adapters/services/aws_nova_sonic_adapter.py +40 -2
- pipecat/adapters/services/bedrock_adapter.py +40 -2
- pipecat/adapters/services/gemini_adapter.py +276 -6
- pipecat/adapters/services/open_ai_adapter.py +88 -7
- pipecat/adapters/services/open_ai_realtime_adapter.py +39 -1
- pipecat/audio/dtmf/__init__.py +0 -0
- pipecat/audio/dtmf/types.py +47 -0
- pipecat/audio/dtmf/utils.py +70 -0
- pipecat/audio/filters/aic_filter.py +199 -0
- pipecat/audio/utils.py +9 -7
- pipecat/extensions/ivr/__init__.py +0 -0
- pipecat/extensions/ivr/ivr_navigator.py +452 -0
- pipecat/frames/frames.py +156 -43
- pipecat/pipeline/llm_switcher.py +76 -0
- pipecat/pipeline/parallel_pipeline.py +3 -3
- pipecat/pipeline/service_switcher.py +144 -0
- pipecat/pipeline/task.py +68 -28
- pipecat/pipeline/task_observer.py +10 -0
- pipecat/processors/aggregators/dtmf_aggregator.py +2 -2
- pipecat/processors/aggregators/llm_context.py +277 -0
- pipecat/processors/aggregators/llm_response.py +48 -15
- pipecat/processors/aggregators/llm_response_universal.py +840 -0
- pipecat/processors/aggregators/openai_llm_context.py +3 -3
- pipecat/processors/dtmf_aggregator.py +0 -2
- pipecat/processors/filters/stt_mute_filter.py +0 -2
- pipecat/processors/frame_processor.py +18 -11
- pipecat/processors/frameworks/rtvi.py +17 -10
- pipecat/processors/metrics/sentry.py +2 -0
- pipecat/runner/daily.py +137 -36
- pipecat/runner/run.py +1 -1
- pipecat/runner/utils.py +7 -7
- pipecat/serializers/asterisk.py +20 -4
- pipecat/serializers/exotel.py +1 -1
- pipecat/serializers/plivo.py +1 -1
- pipecat/serializers/telnyx.py +1 -1
- pipecat/serializers/twilio.py +1 -1
- pipecat/services/__init__.py +2 -2
- pipecat/services/anthropic/llm.py +113 -28
- pipecat/services/asyncai/tts.py +4 -0
- pipecat/services/aws/llm.py +82 -8
- pipecat/services/aws/tts.py +0 -10
- pipecat/services/aws_nova_sonic/aws.py +5 -0
- pipecat/services/cartesia/tts.py +28 -16
- pipecat/services/cerebras/llm.py +15 -10
- pipecat/services/deepgram/stt.py +8 -0
- pipecat/services/deepseek/llm.py +13 -8
- pipecat/services/fireworks/llm.py +13 -8
- pipecat/services/fish/tts.py +8 -6
- pipecat/services/gemini_multimodal_live/gemini.py +5 -0
- pipecat/services/gladia/config.py +7 -1
- pipecat/services/gladia/stt.py +23 -15
- pipecat/services/google/llm.py +159 -59
- pipecat/services/google/llm_openai.py +18 -3
- pipecat/services/grok/llm.py +2 -1
- pipecat/services/llm_service.py +38 -3
- pipecat/services/mem0/memory.py +2 -1
- pipecat/services/mistral/llm.py +5 -6
- pipecat/services/nim/llm.py +2 -1
- pipecat/services/openai/base_llm.py +88 -26
- pipecat/services/openai/image.py +6 -1
- pipecat/services/openai_realtime_beta/openai.py +5 -2
- pipecat/services/openpipe/llm.py +6 -8
- pipecat/services/perplexity/llm.py +13 -8
- pipecat/services/playht/tts.py +9 -6
- pipecat/services/rime/tts.py +1 -1
- pipecat/services/sambanova/llm.py +18 -13
- pipecat/services/sarvam/tts.py +415 -10
- pipecat/services/speechmatics/stt.py +2 -2
- pipecat/services/tavus/video.py +1 -1
- pipecat/services/tts_service.py +15 -5
- pipecat/services/vistaar/llm.py +2 -5
- pipecat/transports/base_input.py +32 -19
- pipecat/transports/base_output.py +39 -5
- pipecat/transports/daily/__init__.py +0 -0
- pipecat/transports/daily/transport.py +2371 -0
- pipecat/transports/daily/utils.py +410 -0
- pipecat/transports/livekit/__init__.py +0 -0
- pipecat/transports/livekit/transport.py +1042 -0
- pipecat/transports/network/fastapi_websocket.py +12 -546
- pipecat/transports/network/small_webrtc.py +12 -922
- pipecat/transports/network/webrtc_connection.py +9 -595
- pipecat/transports/network/websocket_client.py +12 -481
- pipecat/transports/network/websocket_server.py +12 -487
- pipecat/transports/services/daily.py +9 -2334
- pipecat/transports/services/helpers/daily_rest.py +12 -396
- pipecat/transports/services/livekit.py +12 -975
- pipecat/transports/services/tavus.py +12 -757
- pipecat/transports/smallwebrtc/__init__.py +0 -0
- pipecat/transports/smallwebrtc/connection.py +612 -0
- pipecat/transports/smallwebrtc/transport.py +936 -0
- pipecat/transports/tavus/__init__.py +0 -0
- pipecat/transports/tavus/transport.py +770 -0
- pipecat/transports/websocket/__init__.py +0 -0
- pipecat/transports/websocket/client.py +494 -0
- pipecat/transports/websocket/fastapi.py +559 -0
- pipecat/transports/websocket/server.py +500 -0
- pipecat/transports/whatsapp/__init__.py +0 -0
- pipecat/transports/whatsapp/api.py +345 -0
- pipecat/transports/whatsapp/client.py +364 -0
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/WHEEL +0 -0
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/licenses/LICENSE +0 -0
- {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2024–2025, Daily
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
"""WhatsApp API Client.
|
|
8
|
+
|
|
9
|
+
This module provides a client for communicating with the WhatsApp Cloud API,
|
|
10
|
+
handling webhook requests, managing WebRTC connections, and processing
|
|
11
|
+
WhatsApp call events.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
from typing import Awaitable, Callable, Dict, List, Optional
|
|
16
|
+
|
|
17
|
+
import aiohttp
|
|
18
|
+
from loguru import logger
|
|
19
|
+
|
|
20
|
+
from pipecat.transports.smallwebrtc.connection import IceServer, SmallWebRTCConnection
|
|
21
|
+
from pipecat.transports.whatsapp.api import (
|
|
22
|
+
WhatsAppApi,
|
|
23
|
+
WhatsAppConnectCall,
|
|
24
|
+
WhatsAppConnectCallValue,
|
|
25
|
+
WhatsAppTerminateCall,
|
|
26
|
+
WhatsAppTerminateCallValue,
|
|
27
|
+
WhatsAppWebhookRequest,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class WhatsAppClient:
|
|
32
|
+
"""WhatsApp Cloud API client for handling calls and webhook requests.
|
|
33
|
+
|
|
34
|
+
This client manages WhatsApp call connections using WebRTC, processes webhook
|
|
35
|
+
events from WhatsApp, and maintains ongoing call state. It supports both
|
|
36
|
+
incoming call handling and call termination through the WhatsApp Cloud API.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
_whatsapp_api: WhatsApp API instance for making API calls
|
|
40
|
+
_ongoing_calls_map: Dictionary mapping call IDs to WebRTC connections
|
|
41
|
+
_ice_servers: List of ICE servers for WebRTC connections
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
whatsapp_token: str,
|
|
47
|
+
phone_number_id: str,
|
|
48
|
+
session: aiohttp.ClientSession,
|
|
49
|
+
ice_servers: Optional[List[IceServer]] = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Initialize the WhatsApp client.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
whatsapp_token: WhatsApp API access token
|
|
55
|
+
phone_number_id: WhatsApp phone number ID for the business account
|
|
56
|
+
session: aiohttp session for making HTTP requests
|
|
57
|
+
ice_servers: List of ICE servers for WebRTC connections. If None,
|
|
58
|
+
defaults to Google's public STUN server
|
|
59
|
+
"""
|
|
60
|
+
self._whatsapp_api = WhatsAppApi(
|
|
61
|
+
whatsapp_token=whatsapp_token, phone_number_id=phone_number_id, session=session
|
|
62
|
+
)
|
|
63
|
+
self._ongoing_calls_map: Dict[str, SmallWebRTCConnection] = {}
|
|
64
|
+
|
|
65
|
+
# Set default ICE servers if none provided
|
|
66
|
+
if ice_servers is None:
|
|
67
|
+
self._ice_servers = [IceServer(urls="stun:stun.l.google.com:19302")]
|
|
68
|
+
else:
|
|
69
|
+
self._ice_servers = ice_servers
|
|
70
|
+
|
|
71
|
+
async def terminate_all_calls(self) -> None:
|
|
72
|
+
"""Terminate all ongoing WhatsApp calls.
|
|
73
|
+
|
|
74
|
+
This method will:
|
|
75
|
+
1. Send termination requests to WhatsApp API for each ongoing call
|
|
76
|
+
2. Disconnect all WebRTC connections
|
|
77
|
+
3. Clear the ongoing calls map
|
|
78
|
+
|
|
79
|
+
All terminations are executed concurrently for efficiency.
|
|
80
|
+
"""
|
|
81
|
+
logger.debug("Will terminate all ongoing WhatsApp calls")
|
|
82
|
+
|
|
83
|
+
if not self._ongoing_calls_map:
|
|
84
|
+
logger.debug("No ongoing calls to terminate")
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
logger.debug(f"Terminating {len(self._ongoing_calls_map)} ongoing calls")
|
|
88
|
+
|
|
89
|
+
# Terminate each call via WhatsApp API
|
|
90
|
+
termination_tasks = []
|
|
91
|
+
for call_id, pipecat_connection in self._ongoing_calls_map.items():
|
|
92
|
+
logger.debug(f"Terminating call {call_id}")
|
|
93
|
+
# Call WhatsApp API to terminate the call
|
|
94
|
+
if self._whatsapp_api:
|
|
95
|
+
termination_tasks.append(self._whatsapp_api.terminate_call_to_whatsapp(call_id))
|
|
96
|
+
# Disconnect the pipecat connection
|
|
97
|
+
termination_tasks.append(pipecat_connection.disconnect())
|
|
98
|
+
|
|
99
|
+
# Execute all terminations concurrently
|
|
100
|
+
await asyncio.gather(*termination_tasks, return_exceptions=True)
|
|
101
|
+
|
|
102
|
+
# Clear the ongoing calls map
|
|
103
|
+
self._ongoing_calls_map.clear()
|
|
104
|
+
logger.debug("All calls terminated successfully")
|
|
105
|
+
|
|
106
|
+
async def handle_verify_webhook_request(
|
|
107
|
+
self, params: Dict[str, str], expected_verification_token: str
|
|
108
|
+
) -> int:
|
|
109
|
+
"""Handle a verify webhook request from WhatsApp.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
params: Dictionary containing webhook parameters from query string
|
|
113
|
+
expected_verification_token: The expected verification token to validate against
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
int: The challenge value if verification succeeds
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
ValueError: If verification fails due to missing parameters or invalid token
|
|
120
|
+
"""
|
|
121
|
+
mode = params.get("hub.mode")
|
|
122
|
+
challenge = params.get("hub.challenge")
|
|
123
|
+
verify_token = params.get("hub.verify_token")
|
|
124
|
+
|
|
125
|
+
if not mode or not challenge or not verify_token:
|
|
126
|
+
raise ValueError("Missing required webhook verification parameters")
|
|
127
|
+
|
|
128
|
+
if mode != "subscribe":
|
|
129
|
+
raise ValueError(f"Invalid hub mode: expected 'subscribe', got '{mode}'")
|
|
130
|
+
|
|
131
|
+
if verify_token != expected_verification_token:
|
|
132
|
+
raise ValueError("Webhook verification token mismatch")
|
|
133
|
+
|
|
134
|
+
return int(challenge)
|
|
135
|
+
|
|
136
|
+
async def handle_webhook_request(
|
|
137
|
+
self,
|
|
138
|
+
request: WhatsAppWebhookRequest,
|
|
139
|
+
connection_callback: Optional[Callable[[SmallWebRTCConnection], Awaitable[None]]] = None,
|
|
140
|
+
) -> bool:
|
|
141
|
+
"""Handle a webhook request from WhatsApp.
|
|
142
|
+
|
|
143
|
+
This method processes incoming webhook requests and handles both
|
|
144
|
+
connect and terminate events. For connect events, it establishes
|
|
145
|
+
a WebRTC connection and optionally invokes a callback with the
|
|
146
|
+
new connection.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
request: The webhook request from WhatsApp containing call events
|
|
150
|
+
connection_callback: Optional callback function to invoke when a new
|
|
151
|
+
WebRTC connection is established. The callback
|
|
152
|
+
receives the SmallWebRTCConnection instance.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
bool: True if the webhook request was handled successfully, False otherwise
|
|
156
|
+
|
|
157
|
+
Raises:
|
|
158
|
+
ValueError: If the webhook request contains no supported events
|
|
159
|
+
Exception: If connection establishment or API calls fail
|
|
160
|
+
"""
|
|
161
|
+
try:
|
|
162
|
+
for entry in request.entry:
|
|
163
|
+
for change in entry.changes:
|
|
164
|
+
# Handle connect events
|
|
165
|
+
if isinstance(change.value, WhatsAppConnectCallValue):
|
|
166
|
+
for call in change.value.calls:
|
|
167
|
+
if call.event == "connect":
|
|
168
|
+
logger.debug(f"Processing connect event for call {call.id}")
|
|
169
|
+
try:
|
|
170
|
+
connection = await self._handle_connect_event(call)
|
|
171
|
+
|
|
172
|
+
# Invoke callback if provided
|
|
173
|
+
if connection_callback and connection:
|
|
174
|
+
try:
|
|
175
|
+
await connection_callback(connection)
|
|
176
|
+
logger.debug(
|
|
177
|
+
f"Connection callback executed successfully for call {call.id}"
|
|
178
|
+
)
|
|
179
|
+
except Exception as callback_error:
|
|
180
|
+
logger.error(
|
|
181
|
+
f"Connection callback failed for call {call.id}: {callback_error}"
|
|
182
|
+
)
|
|
183
|
+
# Continue execution despite callback failure
|
|
184
|
+
|
|
185
|
+
return True
|
|
186
|
+
except Exception as connect_error:
|
|
187
|
+
logger.error(
|
|
188
|
+
f"Failed to handle connect event for call {call.id}: {connect_error}"
|
|
189
|
+
)
|
|
190
|
+
raise
|
|
191
|
+
|
|
192
|
+
# Handle terminate events
|
|
193
|
+
elif isinstance(change.value, WhatsAppTerminateCallValue):
|
|
194
|
+
for call in change.value.calls:
|
|
195
|
+
if call.event == "terminate":
|
|
196
|
+
logger.debug(f"Processing terminate event for call {call.id}")
|
|
197
|
+
try:
|
|
198
|
+
return await self._handle_terminate_event(call)
|
|
199
|
+
except Exception as terminate_error:
|
|
200
|
+
logger.error(
|
|
201
|
+
f"Failed to handle terminate event for call {call.id}: {terminate_error}"
|
|
202
|
+
)
|
|
203
|
+
raise
|
|
204
|
+
|
|
205
|
+
# No supported events found
|
|
206
|
+
error_msg = "No supported event found in webhook request"
|
|
207
|
+
logger.warning(f"{error_msg}: {request}")
|
|
208
|
+
raise ValueError(error_msg)
|
|
209
|
+
|
|
210
|
+
except Exception as e:
|
|
211
|
+
logger.error(f"Error processing webhook request: {e}")
|
|
212
|
+
logger.debug(f"Webhook request details: {request}")
|
|
213
|
+
raise
|
|
214
|
+
|
|
215
|
+
def _filter_sdp_for_whatsapp(self, sdp: str) -> str:
|
|
216
|
+
"""Filter SDP to be compatible with WhatsApp requirements.
|
|
217
|
+
|
|
218
|
+
WhatsApp only supports SHA-256 fingerprints, so this method removes
|
|
219
|
+
other fingerprint types from the SDP.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
sdp: The original SDP string
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Filtered SDP string compatible with WhatsApp
|
|
226
|
+
"""
|
|
227
|
+
lines = sdp.splitlines()
|
|
228
|
+
filtered = []
|
|
229
|
+
for line in lines:
|
|
230
|
+
if line.startswith("a=fingerprint:") and not line.startswith("a=fingerprint:sha-256"):
|
|
231
|
+
continue # drop sha-384 / sha-512
|
|
232
|
+
filtered.append(line)
|
|
233
|
+
return "\r\n".join(filtered) + "\r\n"
|
|
234
|
+
|
|
235
|
+
async def _handle_connect_event(self, call: WhatsAppConnectCall) -> SmallWebRTCConnection:
|
|
236
|
+
"""Handle a CONNECT event by establishing WebRTC connection and accepting the call.
|
|
237
|
+
|
|
238
|
+
This method:
|
|
239
|
+
1. Creates a new WebRTC connection using configured ICE servers
|
|
240
|
+
2. Initializes the connection with the provided SDP
|
|
241
|
+
3. Generates an SDP answer and filters it for WhatsApp compatibility
|
|
242
|
+
4. Pre-accepts the call with WhatsApp API
|
|
243
|
+
5. Accepts the call with WhatsApp API
|
|
244
|
+
6. Stores the connection for later management
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
call: WhatsApp connect call event
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
The established SmallWebRTCConnection instance
|
|
251
|
+
|
|
252
|
+
Raises:
|
|
253
|
+
Exception: If pre-accept or accept API calls fail
|
|
254
|
+
"""
|
|
255
|
+
logger.debug(f"Incoming call from {call.from_}, call_id: {call.id}")
|
|
256
|
+
|
|
257
|
+
pipecat_connection = None
|
|
258
|
+
try:
|
|
259
|
+
# Create and initialize WebRTC connection
|
|
260
|
+
pipecat_connection = SmallWebRTCConnection(self._ice_servers)
|
|
261
|
+
await pipecat_connection.initialize(sdp=call.session.sdp, type=call.session.sdp_type)
|
|
262
|
+
sdp_answer = pipecat_connection.get_answer().get("sdp")
|
|
263
|
+
sdp_answer = self._filter_sdp_for_whatsapp(sdp_answer)
|
|
264
|
+
|
|
265
|
+
logger.debug(f"SDP answer generated for call {call.id}")
|
|
266
|
+
|
|
267
|
+
# Pre-accept the call
|
|
268
|
+
try:
|
|
269
|
+
pre_accept_resp = await self._whatsapp_api.answer_call_to_whatsapp(
|
|
270
|
+
call.id, "pre_accept", sdp_answer, call.from_
|
|
271
|
+
)
|
|
272
|
+
if not pre_accept_resp.get("success", False):
|
|
273
|
+
logger.error(f"Failed to pre-accept call {call.id}: {pre_accept_resp}")
|
|
274
|
+
raise Exception(f"Failed to pre-accept call: {pre_accept_resp}")
|
|
275
|
+
|
|
276
|
+
logger.debug(f"Pre-accept successful for call {call.id}")
|
|
277
|
+
except Exception as e:
|
|
278
|
+
logger.error(f"Pre-accept API call failed for call {call.id}: {e}")
|
|
279
|
+
raise Exception(f"Failed to pre-accept call: {e}")
|
|
280
|
+
|
|
281
|
+
# Accept the call
|
|
282
|
+
try:
|
|
283
|
+
accept_resp = await self._whatsapp_api.answer_call_to_whatsapp(
|
|
284
|
+
call.id, "accept", sdp_answer, call.from_
|
|
285
|
+
)
|
|
286
|
+
if not accept_resp.get("success", False):
|
|
287
|
+
logger.error(f"Failed to accept call {call.id}: {accept_resp}")
|
|
288
|
+
raise Exception(f"Failed to accept call: {accept_resp}")
|
|
289
|
+
|
|
290
|
+
logger.debug(f"Accept successful for call {call.id}")
|
|
291
|
+
except Exception as e:
|
|
292
|
+
logger.error(f"Accept API call failed for call {call.id}: {e}")
|
|
293
|
+
raise Exception(f"Failed to accept call: {e}")
|
|
294
|
+
|
|
295
|
+
# Store the connection for management
|
|
296
|
+
self._ongoing_calls_map[call.id] = pipecat_connection
|
|
297
|
+
|
|
298
|
+
# Set up disconnect handler
|
|
299
|
+
@pipecat_connection.event_handler("closed")
|
|
300
|
+
async def handle_disconnected(webrtc_connection: SmallWebRTCConnection):
|
|
301
|
+
logger.debug(
|
|
302
|
+
f"Peer connection closed: {webrtc_connection.pc_id} for call {call.id}"
|
|
303
|
+
)
|
|
304
|
+
# Clean up from ongoing calls map
|
|
305
|
+
self._ongoing_calls_map.pop(call.id, None)
|
|
306
|
+
|
|
307
|
+
logger.debug(f"WebRTC connection established successfully for call {call.id}")
|
|
308
|
+
return pipecat_connection
|
|
309
|
+
|
|
310
|
+
except Exception as e:
|
|
311
|
+
# Clean up connection on failure
|
|
312
|
+
if pipecat_connection:
|
|
313
|
+
try:
|
|
314
|
+
await pipecat_connection.disconnect()
|
|
315
|
+
except Exception as cleanup_error:
|
|
316
|
+
logger.error(
|
|
317
|
+
f"Failed to cleanup connection for call {call.id}: {cleanup_error}"
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
logger.error(f"Failed to handle connect event for call {call.id}: {e}")
|
|
321
|
+
raise
|
|
322
|
+
|
|
323
|
+
async def _handle_terminate_event(self, call: WhatsAppTerminateCall) -> bool:
|
|
324
|
+
"""Handle a TERMINATE event by cleaning up resources and logging call completion.
|
|
325
|
+
|
|
326
|
+
This method:
|
|
327
|
+
1. Logs call termination details including duration if available
|
|
328
|
+
2. Disconnects the associated WebRTC connection
|
|
329
|
+
3. Removes the call from the ongoing calls map
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
call: WhatsApp terminate call event
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
bool: True if the call was terminated successfully, False otherwise
|
|
336
|
+
"""
|
|
337
|
+
logger.debug(f"Call terminated from {call.from_}, call_id: {call.id}")
|
|
338
|
+
logger.debug(f"Call status: {call.status}")
|
|
339
|
+
if call.duration:
|
|
340
|
+
logger.debug(f"Call duration: {call.duration} seconds")
|
|
341
|
+
|
|
342
|
+
try:
|
|
343
|
+
if call.id in self._ongoing_calls_map:
|
|
344
|
+
pipecat_connection = self._ongoing_calls_map[call.id]
|
|
345
|
+
logger.debug(f"Disconnecting WebRTC connection for call {call.id}")
|
|
346
|
+
|
|
347
|
+
try:
|
|
348
|
+
await pipecat_connection.disconnect()
|
|
349
|
+
logger.debug(f"WebRTC connection disconnected successfully for call {call.id}")
|
|
350
|
+
except Exception as disconnect_error:
|
|
351
|
+
logger.error(
|
|
352
|
+
f"Failed to disconnect WebRTC connection for call {call.id}: {disconnect_error}"
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
# Remove from ongoing calls map
|
|
356
|
+
self._ongoing_calls_map.pop(call.id, None)
|
|
357
|
+
else:
|
|
358
|
+
logger.warning(f"Call {call.id} not found in ongoing calls map")
|
|
359
|
+
|
|
360
|
+
return True
|
|
361
|
+
|
|
362
|
+
except Exception as e:
|
|
363
|
+
logger.error(f"Error handling terminate event for call {call.id}: {e}")
|
|
364
|
+
return False
|
|
File without changes
|
{dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/top_level.txt
RENAMED
|
File without changes
|