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.

Files changed (106) hide show
  1. {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/METADATA +8 -3
  2. {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/RECORD +106 -79
  3. pipecat/adapters/base_llm_adapter.py +44 -6
  4. pipecat/adapters/services/anthropic_adapter.py +302 -2
  5. pipecat/adapters/services/aws_nova_sonic_adapter.py +40 -2
  6. pipecat/adapters/services/bedrock_adapter.py +40 -2
  7. pipecat/adapters/services/gemini_adapter.py +276 -6
  8. pipecat/adapters/services/open_ai_adapter.py +88 -7
  9. pipecat/adapters/services/open_ai_realtime_adapter.py +39 -1
  10. pipecat/audio/dtmf/__init__.py +0 -0
  11. pipecat/audio/dtmf/types.py +47 -0
  12. pipecat/audio/dtmf/utils.py +70 -0
  13. pipecat/audio/filters/aic_filter.py +199 -0
  14. pipecat/audio/utils.py +9 -7
  15. pipecat/extensions/ivr/__init__.py +0 -0
  16. pipecat/extensions/ivr/ivr_navigator.py +452 -0
  17. pipecat/frames/frames.py +156 -43
  18. pipecat/pipeline/llm_switcher.py +76 -0
  19. pipecat/pipeline/parallel_pipeline.py +3 -3
  20. pipecat/pipeline/service_switcher.py +144 -0
  21. pipecat/pipeline/task.py +68 -28
  22. pipecat/pipeline/task_observer.py +10 -0
  23. pipecat/processors/aggregators/dtmf_aggregator.py +2 -2
  24. pipecat/processors/aggregators/llm_context.py +277 -0
  25. pipecat/processors/aggregators/llm_response.py +48 -15
  26. pipecat/processors/aggregators/llm_response_universal.py +840 -0
  27. pipecat/processors/aggregators/openai_llm_context.py +3 -3
  28. pipecat/processors/dtmf_aggregator.py +0 -2
  29. pipecat/processors/filters/stt_mute_filter.py +0 -2
  30. pipecat/processors/frame_processor.py +18 -11
  31. pipecat/processors/frameworks/rtvi.py +17 -10
  32. pipecat/processors/metrics/sentry.py +2 -0
  33. pipecat/runner/daily.py +137 -36
  34. pipecat/runner/run.py +1 -1
  35. pipecat/runner/utils.py +7 -7
  36. pipecat/serializers/asterisk.py +20 -4
  37. pipecat/serializers/exotel.py +1 -1
  38. pipecat/serializers/plivo.py +1 -1
  39. pipecat/serializers/telnyx.py +1 -1
  40. pipecat/serializers/twilio.py +1 -1
  41. pipecat/services/__init__.py +2 -2
  42. pipecat/services/anthropic/llm.py +113 -28
  43. pipecat/services/asyncai/tts.py +4 -0
  44. pipecat/services/aws/llm.py +82 -8
  45. pipecat/services/aws/tts.py +0 -10
  46. pipecat/services/aws_nova_sonic/aws.py +5 -0
  47. pipecat/services/cartesia/tts.py +28 -16
  48. pipecat/services/cerebras/llm.py +15 -10
  49. pipecat/services/deepgram/stt.py +8 -0
  50. pipecat/services/deepseek/llm.py +13 -8
  51. pipecat/services/fireworks/llm.py +13 -8
  52. pipecat/services/fish/tts.py +8 -6
  53. pipecat/services/gemini_multimodal_live/gemini.py +5 -0
  54. pipecat/services/gladia/config.py +7 -1
  55. pipecat/services/gladia/stt.py +23 -15
  56. pipecat/services/google/llm.py +159 -59
  57. pipecat/services/google/llm_openai.py +18 -3
  58. pipecat/services/grok/llm.py +2 -1
  59. pipecat/services/llm_service.py +38 -3
  60. pipecat/services/mem0/memory.py +2 -1
  61. pipecat/services/mistral/llm.py +5 -6
  62. pipecat/services/nim/llm.py +2 -1
  63. pipecat/services/openai/base_llm.py +88 -26
  64. pipecat/services/openai/image.py +6 -1
  65. pipecat/services/openai_realtime_beta/openai.py +5 -2
  66. pipecat/services/openpipe/llm.py +6 -8
  67. pipecat/services/perplexity/llm.py +13 -8
  68. pipecat/services/playht/tts.py +9 -6
  69. pipecat/services/rime/tts.py +1 -1
  70. pipecat/services/sambanova/llm.py +18 -13
  71. pipecat/services/sarvam/tts.py +415 -10
  72. pipecat/services/speechmatics/stt.py +2 -2
  73. pipecat/services/tavus/video.py +1 -1
  74. pipecat/services/tts_service.py +15 -5
  75. pipecat/services/vistaar/llm.py +2 -5
  76. pipecat/transports/base_input.py +32 -19
  77. pipecat/transports/base_output.py +39 -5
  78. pipecat/transports/daily/__init__.py +0 -0
  79. pipecat/transports/daily/transport.py +2371 -0
  80. pipecat/transports/daily/utils.py +410 -0
  81. pipecat/transports/livekit/__init__.py +0 -0
  82. pipecat/transports/livekit/transport.py +1042 -0
  83. pipecat/transports/network/fastapi_websocket.py +12 -546
  84. pipecat/transports/network/small_webrtc.py +12 -922
  85. pipecat/transports/network/webrtc_connection.py +9 -595
  86. pipecat/transports/network/websocket_client.py +12 -481
  87. pipecat/transports/network/websocket_server.py +12 -487
  88. pipecat/transports/services/daily.py +9 -2334
  89. pipecat/transports/services/helpers/daily_rest.py +12 -396
  90. pipecat/transports/services/livekit.py +12 -975
  91. pipecat/transports/services/tavus.py +12 -757
  92. pipecat/transports/smallwebrtc/__init__.py +0 -0
  93. pipecat/transports/smallwebrtc/connection.py +612 -0
  94. pipecat/transports/smallwebrtc/transport.py +936 -0
  95. pipecat/transports/tavus/__init__.py +0 -0
  96. pipecat/transports/tavus/transport.py +770 -0
  97. pipecat/transports/websocket/__init__.py +0 -0
  98. pipecat/transports/websocket/client.py +494 -0
  99. pipecat/transports/websocket/fastapi.py +559 -0
  100. pipecat/transports/websocket/server.py +500 -0
  101. pipecat/transports/whatsapp/__init__.py +0 -0
  102. pipecat/transports/whatsapp/api.py +345 -0
  103. pipecat/transports/whatsapp/client.py +364 -0
  104. {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/WHEEL +0 -0
  105. {dv_pipecat_ai-0.0.82.dev815.dist-info → dv_pipecat_ai-0.0.82.dev857.dist-info}/licenses/LICENSE +0 -0
  106. {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()