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,345 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2024–2025, Daily
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
"""WhatsApp API.
|
|
8
|
+
|
|
9
|
+
API to communicate with WhatsApp Cloud API.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Any, Dict, List, Optional, Union
|
|
13
|
+
|
|
14
|
+
import aiohttp
|
|
15
|
+
from loguru import logger
|
|
16
|
+
from pydantic import BaseModel, Field
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ----------------------------
|
|
20
|
+
# Pydantic Models for WhatsApp
|
|
21
|
+
# ----------------------------
|
|
22
|
+
class WhatsAppSession(BaseModel):
|
|
23
|
+
"""WebRTC session information for WhatsApp calls.
|
|
24
|
+
|
|
25
|
+
Parameters:
|
|
26
|
+
sdp: Session Description Protocol (SDP) data for WebRTC connection
|
|
27
|
+
sdp_type: Type of SDP (e.g., "offer", "answer")
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
sdp: str
|
|
31
|
+
sdp_type: str
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class WhatsAppError(BaseModel):
|
|
35
|
+
"""Error information from WhatsApp API responses.
|
|
36
|
+
|
|
37
|
+
Parameters:
|
|
38
|
+
code: Error code number
|
|
39
|
+
message: Human-readable error message
|
|
40
|
+
href: URL for more information about the error
|
|
41
|
+
error_data: Additional error-specific data
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
code: int
|
|
45
|
+
message: str
|
|
46
|
+
href: str
|
|
47
|
+
error_data: Dict[str, Any]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class WhatsAppConnectCall(BaseModel):
|
|
51
|
+
"""Incoming call connection event data.
|
|
52
|
+
|
|
53
|
+
Represents a user-initiated call that requires handling. This is sent
|
|
54
|
+
when a WhatsApp user initiates a call to your business number.
|
|
55
|
+
|
|
56
|
+
Parameters:
|
|
57
|
+
id: Unique call identifier
|
|
58
|
+
from_: Phone number of the caller (WhatsApp ID format)
|
|
59
|
+
to: Your business phone number that received the call
|
|
60
|
+
event: Always "connect" for incoming calls
|
|
61
|
+
timestamp: ISO 8601 timestamp when the call was initiated
|
|
62
|
+
direction: Optional call direction ("inbound" for user-initiated calls)
|
|
63
|
+
session: WebRTC session data containing SDP offer from the caller
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
id: str
|
|
67
|
+
from_: str = Field(..., alias="from")
|
|
68
|
+
to: str
|
|
69
|
+
event: str # "connect"
|
|
70
|
+
timestamp: str
|
|
71
|
+
direction: Optional[str]
|
|
72
|
+
session: WhatsAppSession
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class WhatsAppTerminateCall(BaseModel):
|
|
76
|
+
"""Call termination event data.
|
|
77
|
+
|
|
78
|
+
Represents the end of a call session, whether completed successfully,
|
|
79
|
+
failed, or was rejected by either party.
|
|
80
|
+
|
|
81
|
+
Parameters:
|
|
82
|
+
id: Unique call identifier (matches the connect event)
|
|
83
|
+
from_: Phone number of the caller
|
|
84
|
+
to: Your business phone number
|
|
85
|
+
event: Always "terminate" for call end events
|
|
86
|
+
timestamp: ISO 8601 timestamp when the call ended
|
|
87
|
+
direction: Optional call direction
|
|
88
|
+
biz_opaque_callback_data: Optional business-specific callback data
|
|
89
|
+
status: Call completion status ("FAILED", "COMPLETED", "REJECTED")
|
|
90
|
+
start_time: ISO 8601 timestamp when call actually started (after acceptance)
|
|
91
|
+
end_time: ISO 8601 timestamp when call ended
|
|
92
|
+
duration: Call duration in seconds (only for completed calls)
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
id: str
|
|
96
|
+
from_: str = Field(..., alias="from")
|
|
97
|
+
to: str
|
|
98
|
+
event: str # "terminate"
|
|
99
|
+
timestamp: str
|
|
100
|
+
direction: Optional[str]
|
|
101
|
+
biz_opaque_callback_data: Optional[str] = None
|
|
102
|
+
status: Optional[str] = None # "FAILED" or "COMPLETED" or "REJECTED"
|
|
103
|
+
start_time: Optional[str] = None
|
|
104
|
+
end_time: Optional[str] = None
|
|
105
|
+
duration: Optional[int] = None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class WhatsAppProfile(BaseModel):
|
|
109
|
+
"""User profile information.
|
|
110
|
+
|
|
111
|
+
Parameters:
|
|
112
|
+
name: Display name of the WhatsApp user
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
name: str
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class WhatsAppContact(BaseModel):
|
|
119
|
+
"""Contact information for a WhatsApp user.
|
|
120
|
+
|
|
121
|
+
Parameters:
|
|
122
|
+
profile: User's profile information
|
|
123
|
+
wa_id: WhatsApp ID (phone number in international format without +)
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
profile: WhatsAppProfile
|
|
127
|
+
wa_id: str
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class WhatsAppMetadata(BaseModel):
|
|
131
|
+
"""Business phone number metadata.
|
|
132
|
+
|
|
133
|
+
Parameters:
|
|
134
|
+
display_phone_number: Formatted phone number for display
|
|
135
|
+
phone_number_id: WhatsApp Business API phone number ID
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
display_phone_number: str
|
|
139
|
+
phone_number_id: str
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class WhatsAppConnectCallValue(BaseModel):
|
|
143
|
+
"""Webhook payload for incoming call events.
|
|
144
|
+
|
|
145
|
+
Parameters:
|
|
146
|
+
messaging_product: Always "whatsapp"
|
|
147
|
+
metadata: Business phone number information
|
|
148
|
+
contacts: List of contact information for involved parties
|
|
149
|
+
calls: List of call connection events
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
messaging_product: str
|
|
153
|
+
metadata: WhatsAppMetadata
|
|
154
|
+
contacts: List[WhatsAppContact]
|
|
155
|
+
calls: List[WhatsAppConnectCall]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class WhatsAppTerminateCallValue(BaseModel):
|
|
159
|
+
"""Webhook payload for call termination events.
|
|
160
|
+
|
|
161
|
+
Parameters:
|
|
162
|
+
messaging_product: Always "whatsapp"
|
|
163
|
+
metadata: Business phone number information
|
|
164
|
+
calls: List of call termination events
|
|
165
|
+
errors: Optional list of errors that occurred during the call
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
messaging_product: str
|
|
169
|
+
metadata: WhatsAppMetadata
|
|
170
|
+
calls: List[WhatsAppTerminateCall]
|
|
171
|
+
errors: Optional[List[WhatsAppError]] = None
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class WhatsAppChange(BaseModel):
|
|
175
|
+
"""Webhook change event wrapper.
|
|
176
|
+
|
|
177
|
+
Parameters:
|
|
178
|
+
value: The actual event data (connect or terminate)
|
|
179
|
+
field: Always "calls" for calling webhooks
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
value: Union[WhatsAppConnectCallValue, WhatsAppTerminateCallValue]
|
|
183
|
+
field: str
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class WhatsAppEntry(BaseModel):
|
|
187
|
+
"""Webhook entry containing one or more changes.
|
|
188
|
+
|
|
189
|
+
Parameters:
|
|
190
|
+
id: WhatsApp Business Account ID
|
|
191
|
+
changes: List of change events in this webhook delivery
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
id: str
|
|
195
|
+
changes: List[WhatsAppChange]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class WhatsAppWebhookRequest(BaseModel):
|
|
199
|
+
"""Complete webhook request from WhatsApp.
|
|
200
|
+
|
|
201
|
+
This is the top-level structure for all webhook deliveries from
|
|
202
|
+
the WhatsApp Cloud API for calling events.
|
|
203
|
+
|
|
204
|
+
Parameters:
|
|
205
|
+
object: Always "whatsapp_business_account"
|
|
206
|
+
entry: List of webhook entries (usually contains one entry)
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
object: str
|
|
210
|
+
entry: List[WhatsAppEntry]
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class WhatsAppApi:
|
|
214
|
+
"""WhatsApp Cloud API client for handling calls.
|
|
215
|
+
|
|
216
|
+
This class provides methods to interact with the WhatsApp Cloud API
|
|
217
|
+
for managing voice calls, including answering, rejecting, and terminating calls.
|
|
218
|
+
|
|
219
|
+
Parameters:
|
|
220
|
+
BASE_URL: Base URL for WhatsApp Graph API v23.0
|
|
221
|
+
phone_number_id: Your WhatsApp Business phone number ID
|
|
222
|
+
session: aiohttp client session for making HTTP requests
|
|
223
|
+
whatsapp_url: Complete URL for the calls endpoint
|
|
224
|
+
whatsapp_token: Bearer token for API authentication
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
BASE_URL = f"https://graph.facebook.com/v23.0/"
|
|
228
|
+
|
|
229
|
+
def __init__(
|
|
230
|
+
self, whatsapp_token: str, phone_number_id: str, session: aiohttp.ClientSession
|
|
231
|
+
) -> None:
|
|
232
|
+
"""Initialize the WhatsApp API client.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
whatsapp_token: WhatsApp access token for authentication
|
|
236
|
+
phone_number_id: Your business phone number ID from WhatsApp Business API
|
|
237
|
+
session: aiohttp ClientSession for making HTTP requests
|
|
238
|
+
"""
|
|
239
|
+
self._phone_number_id = phone_number_id
|
|
240
|
+
self._session = session
|
|
241
|
+
self._whatsapp_url = f"{self.BASE_URL}{phone_number_id}/calls"
|
|
242
|
+
self._whatsapp_token = whatsapp_token
|
|
243
|
+
|
|
244
|
+
async def answer_call_to_whatsapp(self, call_id: str, action: str, sdp: str, from_: str):
|
|
245
|
+
"""Answer an incoming WhatsApp call.
|
|
246
|
+
|
|
247
|
+
This method handles the call answering process, supporting both "pre_accept"
|
|
248
|
+
and "accept" actions as required by the WhatsApp calling workflow.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
call_id: Unique identifier for the call (from connect webhook)
|
|
252
|
+
action: Action to perform ("pre_accept" or "accept")
|
|
253
|
+
sdp: Session Description Protocol answer for WebRTC connection
|
|
254
|
+
from_: Caller's phone number (WhatsApp ID format)
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
Dict containing the API response with success status and any error details
|
|
258
|
+
|
|
259
|
+
Note:
|
|
260
|
+
Calls must be pre-accepted before being accepted. The typical flow is:
|
|
261
|
+
1. Receive connect webhook
|
|
262
|
+
2. Call with action="pre_accept"
|
|
263
|
+
3. Call with action="accept"
|
|
264
|
+
"""
|
|
265
|
+
logger.debug(f"Answering call {call_id} to WhatsApp, action:{action}")
|
|
266
|
+
async with self._session.post(
|
|
267
|
+
self._whatsapp_url,
|
|
268
|
+
headers={
|
|
269
|
+
"Authorization": f"Bearer {self._whatsapp_token}",
|
|
270
|
+
"Content-Type": "application/json",
|
|
271
|
+
},
|
|
272
|
+
json={
|
|
273
|
+
"messaging_product": "whatsapp",
|
|
274
|
+
"to": from_,
|
|
275
|
+
"action": action,
|
|
276
|
+
"call_id": call_id,
|
|
277
|
+
"session": {"sdp": sdp, "sdp_type": "answer"},
|
|
278
|
+
},
|
|
279
|
+
) as response:
|
|
280
|
+
return await response.json()
|
|
281
|
+
|
|
282
|
+
async def reject_call_to_whatsapp(self, call_id: str):
|
|
283
|
+
"""Reject an incoming WhatsApp call.
|
|
284
|
+
|
|
285
|
+
This method rejects a call that was received via connect webhook.
|
|
286
|
+
The caller will receive a rejection notification and a terminate
|
|
287
|
+
webhook will be sent with status "REJECTED".
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
call_id: Unique identifier for the call (from connect webhook)
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Dict containing the API response with success status and any error details
|
|
294
|
+
|
|
295
|
+
Note:
|
|
296
|
+
This should be called instead of answer_call_to_whatsapp when you want
|
|
297
|
+
to decline the incoming call. The caller will see the call as rejected.
|
|
298
|
+
"""
|
|
299
|
+
logger.debug(f"Rejecting call {call_id}")
|
|
300
|
+
async with self._session.post(
|
|
301
|
+
self._whatsapp_url,
|
|
302
|
+
headers={
|
|
303
|
+
"Authorization": f"Bearer {self._whatsapp_token}",
|
|
304
|
+
"Content-Type": "application/json",
|
|
305
|
+
},
|
|
306
|
+
json={
|
|
307
|
+
"messaging_product": "whatsapp",
|
|
308
|
+
"action": "reject",
|
|
309
|
+
"call_id": call_id,
|
|
310
|
+
},
|
|
311
|
+
) as response:
|
|
312
|
+
return await response.json()
|
|
313
|
+
|
|
314
|
+
async def terminate_call_to_whatsapp(self, call_id: str):
|
|
315
|
+
"""Terminate an active WhatsApp call.
|
|
316
|
+
|
|
317
|
+
This method ends an ongoing call that has been previously accepted.
|
|
318
|
+
Both parties will be disconnected and a terminate webhook will be
|
|
319
|
+
sent with status "COMPLETED".
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
call_id: Unique identifier for the active call
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Dict containing the API response with success status and any error details
|
|
326
|
+
|
|
327
|
+
Note:
|
|
328
|
+
This should only be called for calls that have been accepted and are
|
|
329
|
+
currently active. For incoming calls that haven't been accepted yet,
|
|
330
|
+
use reject_call_to_whatsapp instead.
|
|
331
|
+
"""
|
|
332
|
+
logger.debug(f"Terminating call {call_id}")
|
|
333
|
+
async with self._session.post(
|
|
334
|
+
self._whatsapp_url,
|
|
335
|
+
headers={
|
|
336
|
+
"Authorization": f"Bearer {self._whatsapp_token}",
|
|
337
|
+
"Content-Type": "application/json",
|
|
338
|
+
},
|
|
339
|
+
json={
|
|
340
|
+
"messaging_product": "whatsapp",
|
|
341
|
+
"action": "terminate",
|
|
342
|
+
"call_id": call_id,
|
|
343
|
+
},
|
|
344
|
+
) as response:
|
|
345
|
+
return await response.json()
|