dv-pipecat-ai 0.0.74.dev770__py3-none-any.whl → 0.0.82.dev776__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 (244) hide show
  1. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/METADATA +137 -93
  2. dv_pipecat_ai-0.0.82.dev776.dist-info/RECORD +340 -0
  3. pipecat/__init__.py +17 -0
  4. pipecat/adapters/base_llm_adapter.py +36 -1
  5. pipecat/adapters/schemas/direct_function.py +296 -0
  6. pipecat/adapters/schemas/function_schema.py +15 -6
  7. pipecat/adapters/schemas/tools_schema.py +55 -7
  8. pipecat/adapters/services/anthropic_adapter.py +22 -3
  9. pipecat/adapters/services/aws_nova_sonic_adapter.py +23 -3
  10. pipecat/adapters/services/bedrock_adapter.py +22 -3
  11. pipecat/adapters/services/gemini_adapter.py +16 -3
  12. pipecat/adapters/services/open_ai_adapter.py +17 -2
  13. pipecat/adapters/services/open_ai_realtime_adapter.py +23 -3
  14. pipecat/audio/filters/base_audio_filter.py +30 -6
  15. pipecat/audio/filters/koala_filter.py +37 -2
  16. pipecat/audio/filters/krisp_filter.py +59 -6
  17. pipecat/audio/filters/noisereduce_filter.py +37 -0
  18. pipecat/audio/interruptions/base_interruption_strategy.py +25 -5
  19. pipecat/audio/interruptions/min_words_interruption_strategy.py +21 -4
  20. pipecat/audio/mixers/base_audio_mixer.py +30 -7
  21. pipecat/audio/mixers/soundfile_mixer.py +53 -6
  22. pipecat/audio/resamplers/base_audio_resampler.py +17 -9
  23. pipecat/audio/resamplers/resampy_resampler.py +26 -1
  24. pipecat/audio/resamplers/soxr_resampler.py +32 -1
  25. pipecat/audio/resamplers/soxr_stream_resampler.py +101 -0
  26. pipecat/audio/utils.py +194 -1
  27. pipecat/audio/vad/silero.py +60 -3
  28. pipecat/audio/vad/vad_analyzer.py +114 -30
  29. pipecat/clocks/base_clock.py +19 -0
  30. pipecat/clocks/system_clock.py +25 -0
  31. pipecat/extensions/voicemail/__init__.py +0 -0
  32. pipecat/extensions/voicemail/voicemail_detector.py +707 -0
  33. pipecat/frames/frames.py +590 -156
  34. pipecat/metrics/metrics.py +64 -1
  35. pipecat/observers/base_observer.py +58 -19
  36. pipecat/observers/loggers/debug_log_observer.py +56 -64
  37. pipecat/observers/loggers/llm_log_observer.py +8 -1
  38. pipecat/observers/loggers/transcription_log_observer.py +19 -7
  39. pipecat/observers/loggers/user_bot_latency_log_observer.py +32 -5
  40. pipecat/observers/turn_tracking_observer.py +26 -1
  41. pipecat/pipeline/base_pipeline.py +5 -7
  42. pipecat/pipeline/base_task.py +52 -9
  43. pipecat/pipeline/parallel_pipeline.py +121 -177
  44. pipecat/pipeline/pipeline.py +129 -20
  45. pipecat/pipeline/runner.py +50 -1
  46. pipecat/pipeline/sync_parallel_pipeline.py +132 -32
  47. pipecat/pipeline/task.py +263 -280
  48. pipecat/pipeline/task_observer.py +85 -34
  49. pipecat/pipeline/to_be_updated/merge_pipeline.py +32 -2
  50. pipecat/processors/aggregators/dtmf_aggregator.py +29 -22
  51. pipecat/processors/aggregators/gated.py +25 -24
  52. pipecat/processors/aggregators/gated_openai_llm_context.py +22 -2
  53. pipecat/processors/aggregators/llm_response.py +398 -89
  54. pipecat/processors/aggregators/openai_llm_context.py +161 -13
  55. pipecat/processors/aggregators/sentence.py +25 -14
  56. pipecat/processors/aggregators/user_response.py +28 -3
  57. pipecat/processors/aggregators/vision_image_frame.py +24 -14
  58. pipecat/processors/async_generator.py +28 -0
  59. pipecat/processors/audio/audio_buffer_processor.py +78 -37
  60. pipecat/processors/consumer_processor.py +25 -6
  61. pipecat/processors/filters/frame_filter.py +23 -0
  62. pipecat/processors/filters/function_filter.py +30 -0
  63. pipecat/processors/filters/identity_filter.py +17 -2
  64. pipecat/processors/filters/null_filter.py +24 -1
  65. pipecat/processors/filters/stt_mute_filter.py +56 -21
  66. pipecat/processors/filters/wake_check_filter.py +46 -3
  67. pipecat/processors/filters/wake_notifier_filter.py +21 -3
  68. pipecat/processors/frame_processor.py +488 -131
  69. pipecat/processors/frameworks/langchain.py +38 -3
  70. pipecat/processors/frameworks/rtvi.py +719 -34
  71. pipecat/processors/gstreamer/pipeline_source.py +41 -0
  72. pipecat/processors/idle_frame_processor.py +26 -3
  73. pipecat/processors/logger.py +23 -0
  74. pipecat/processors/metrics/frame_processor_metrics.py +77 -4
  75. pipecat/processors/metrics/sentry.py +42 -4
  76. pipecat/processors/producer_processor.py +34 -14
  77. pipecat/processors/text_transformer.py +22 -10
  78. pipecat/processors/transcript_processor.py +48 -29
  79. pipecat/processors/user_idle_processor.py +31 -21
  80. pipecat/runner/__init__.py +1 -0
  81. pipecat/runner/daily.py +132 -0
  82. pipecat/runner/livekit.py +148 -0
  83. pipecat/runner/run.py +543 -0
  84. pipecat/runner/types.py +67 -0
  85. pipecat/runner/utils.py +515 -0
  86. pipecat/serializers/base_serializer.py +42 -0
  87. pipecat/serializers/exotel.py +17 -6
  88. pipecat/serializers/genesys.py +95 -0
  89. pipecat/serializers/livekit.py +33 -0
  90. pipecat/serializers/plivo.py +16 -15
  91. pipecat/serializers/protobuf.py +37 -1
  92. pipecat/serializers/telnyx.py +18 -17
  93. pipecat/serializers/twilio.py +32 -16
  94. pipecat/services/ai_service.py +5 -3
  95. pipecat/services/anthropic/llm.py +113 -43
  96. pipecat/services/assemblyai/models.py +63 -5
  97. pipecat/services/assemblyai/stt.py +64 -11
  98. pipecat/services/asyncai/__init__.py +0 -0
  99. pipecat/services/asyncai/tts.py +501 -0
  100. pipecat/services/aws/llm.py +185 -111
  101. pipecat/services/aws/stt.py +217 -23
  102. pipecat/services/aws/tts.py +118 -52
  103. pipecat/services/aws/utils.py +101 -5
  104. pipecat/services/aws_nova_sonic/aws.py +82 -64
  105. pipecat/services/aws_nova_sonic/context.py +15 -6
  106. pipecat/services/azure/common.py +10 -2
  107. pipecat/services/azure/image.py +32 -0
  108. pipecat/services/azure/llm.py +9 -7
  109. pipecat/services/azure/stt.py +65 -2
  110. pipecat/services/azure/tts.py +154 -23
  111. pipecat/services/cartesia/stt.py +125 -8
  112. pipecat/services/cartesia/tts.py +102 -38
  113. pipecat/services/cerebras/llm.py +15 -23
  114. pipecat/services/deepgram/stt.py +19 -11
  115. pipecat/services/deepgram/tts.py +36 -0
  116. pipecat/services/deepseek/llm.py +14 -23
  117. pipecat/services/elevenlabs/tts.py +330 -64
  118. pipecat/services/fal/image.py +43 -0
  119. pipecat/services/fal/stt.py +48 -10
  120. pipecat/services/fireworks/llm.py +14 -21
  121. pipecat/services/fish/tts.py +109 -9
  122. pipecat/services/gemini_multimodal_live/__init__.py +1 -0
  123. pipecat/services/gemini_multimodal_live/events.py +83 -2
  124. pipecat/services/gemini_multimodal_live/file_api.py +189 -0
  125. pipecat/services/gemini_multimodal_live/gemini.py +218 -21
  126. pipecat/services/gladia/config.py +17 -10
  127. pipecat/services/gladia/stt.py +82 -36
  128. pipecat/services/google/frames.py +40 -0
  129. pipecat/services/google/google.py +2 -0
  130. pipecat/services/google/image.py +39 -2
  131. pipecat/services/google/llm.py +176 -58
  132. pipecat/services/google/llm_openai.py +26 -4
  133. pipecat/services/google/llm_vertex.py +37 -15
  134. pipecat/services/google/rtvi.py +41 -0
  135. pipecat/services/google/stt.py +65 -17
  136. pipecat/services/google/test-google-chirp.py +45 -0
  137. pipecat/services/google/tts.py +390 -19
  138. pipecat/services/grok/llm.py +8 -6
  139. pipecat/services/groq/llm.py +8 -6
  140. pipecat/services/groq/stt.py +13 -9
  141. pipecat/services/groq/tts.py +40 -0
  142. pipecat/services/hamsa/__init__.py +9 -0
  143. pipecat/services/hamsa/stt.py +241 -0
  144. pipecat/services/heygen/__init__.py +5 -0
  145. pipecat/services/heygen/api.py +281 -0
  146. pipecat/services/heygen/client.py +620 -0
  147. pipecat/services/heygen/video.py +338 -0
  148. pipecat/services/image_service.py +5 -3
  149. pipecat/services/inworld/__init__.py +1 -0
  150. pipecat/services/inworld/tts.py +592 -0
  151. pipecat/services/llm_service.py +127 -45
  152. pipecat/services/lmnt/tts.py +80 -7
  153. pipecat/services/mcp_service.py +85 -44
  154. pipecat/services/mem0/memory.py +42 -13
  155. pipecat/services/minimax/tts.py +74 -15
  156. pipecat/services/mistral/__init__.py +0 -0
  157. pipecat/services/mistral/llm.py +185 -0
  158. pipecat/services/moondream/vision.py +55 -10
  159. pipecat/services/neuphonic/tts.py +275 -48
  160. pipecat/services/nim/llm.py +8 -6
  161. pipecat/services/ollama/llm.py +27 -7
  162. pipecat/services/openai/base_llm.py +54 -16
  163. pipecat/services/openai/image.py +30 -0
  164. pipecat/services/openai/llm.py +7 -5
  165. pipecat/services/openai/stt.py +13 -9
  166. pipecat/services/openai/tts.py +42 -10
  167. pipecat/services/openai_realtime_beta/azure.py +11 -9
  168. pipecat/services/openai_realtime_beta/context.py +7 -5
  169. pipecat/services/openai_realtime_beta/events.py +10 -7
  170. pipecat/services/openai_realtime_beta/openai.py +37 -18
  171. pipecat/services/openpipe/llm.py +30 -24
  172. pipecat/services/openrouter/llm.py +9 -7
  173. pipecat/services/perplexity/llm.py +15 -19
  174. pipecat/services/piper/tts.py +26 -12
  175. pipecat/services/playht/tts.py +227 -65
  176. pipecat/services/qwen/llm.py +8 -6
  177. pipecat/services/rime/tts.py +128 -17
  178. pipecat/services/riva/stt.py +160 -22
  179. pipecat/services/riva/tts.py +67 -2
  180. pipecat/services/sambanova/llm.py +19 -17
  181. pipecat/services/sambanova/stt.py +14 -8
  182. pipecat/services/sarvam/tts.py +60 -13
  183. pipecat/services/simli/video.py +82 -21
  184. pipecat/services/soniox/__init__.py +0 -0
  185. pipecat/services/soniox/stt.py +398 -0
  186. pipecat/services/speechmatics/stt.py +29 -17
  187. pipecat/services/stt_service.py +47 -11
  188. pipecat/services/tavus/video.py +94 -25
  189. pipecat/services/together/llm.py +8 -6
  190. pipecat/services/tts_service.py +77 -53
  191. pipecat/services/ultravox/stt.py +46 -43
  192. pipecat/services/vision_service.py +5 -3
  193. pipecat/services/websocket_service.py +12 -11
  194. pipecat/services/whisper/base_stt.py +58 -12
  195. pipecat/services/whisper/stt.py +69 -58
  196. pipecat/services/xtts/tts.py +59 -2
  197. pipecat/sync/base_notifier.py +19 -0
  198. pipecat/sync/event_notifier.py +24 -0
  199. pipecat/tests/utils.py +73 -5
  200. pipecat/transcriptions/language.py +24 -0
  201. pipecat/transports/base_input.py +112 -8
  202. pipecat/transports/base_output.py +235 -13
  203. pipecat/transports/base_transport.py +119 -0
  204. pipecat/transports/local/audio.py +76 -0
  205. pipecat/transports/local/tk.py +84 -0
  206. pipecat/transports/network/fastapi_websocket.py +174 -15
  207. pipecat/transports/network/small_webrtc.py +383 -39
  208. pipecat/transports/network/webrtc_connection.py +214 -8
  209. pipecat/transports/network/websocket_client.py +171 -1
  210. pipecat/transports/network/websocket_server.py +147 -9
  211. pipecat/transports/services/daily.py +792 -70
  212. pipecat/transports/services/helpers/daily_rest.py +122 -129
  213. pipecat/transports/services/livekit.py +339 -4
  214. pipecat/transports/services/tavus.py +273 -38
  215. pipecat/utils/asyncio/task_manager.py +92 -186
  216. pipecat/utils/base_object.py +83 -1
  217. pipecat/utils/network.py +2 -0
  218. pipecat/utils/string.py +114 -58
  219. pipecat/utils/text/base_text_aggregator.py +44 -13
  220. pipecat/utils/text/base_text_filter.py +46 -0
  221. pipecat/utils/text/markdown_text_filter.py +70 -14
  222. pipecat/utils/text/pattern_pair_aggregator.py +18 -14
  223. pipecat/utils/text/simple_text_aggregator.py +43 -2
  224. pipecat/utils/text/skip_tags_aggregator.py +21 -13
  225. pipecat/utils/time.py +36 -0
  226. pipecat/utils/tracing/class_decorators.py +32 -7
  227. pipecat/utils/tracing/conversation_context_provider.py +12 -2
  228. pipecat/utils/tracing/service_attributes.py +80 -64
  229. pipecat/utils/tracing/service_decorators.py +48 -21
  230. pipecat/utils/tracing/setup.py +13 -7
  231. pipecat/utils/tracing/turn_context_provider.py +12 -2
  232. pipecat/utils/tracing/turn_trace_observer.py +27 -0
  233. pipecat/utils/utils.py +14 -14
  234. dv_pipecat_ai-0.0.74.dev770.dist-info/RECORD +0 -319
  235. pipecat/examples/daily_runner.py +0 -64
  236. pipecat/examples/run.py +0 -265
  237. pipecat/utils/asyncio/watchdog_async_iterator.py +0 -72
  238. pipecat/utils/asyncio/watchdog_event.py +0 -42
  239. pipecat/utils/asyncio/watchdog_priority_queue.py +0 -48
  240. pipecat/utils/asyncio/watchdog_queue.py +0 -48
  241. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/WHEEL +0 -0
  242. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/licenses/LICENSE +0 -0
  243. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/top_level.txt +0 -0
  244. /pipecat/{examples → extensions}/__init__.py +0 -0
@@ -1,3 +1,16 @@
1
+ #
2
+ # Copyright (c) 2024–2025, Daily
3
+ #
4
+ # SPDX-License-Identifier: BSD 2-Clause License
5
+ #
6
+
7
+ """Tavus transport implementation for Pipecat.
8
+
9
+ This module provides integration with the Tavus platform for creating conversational
10
+ AI applications with avatars. It manages conversation sessions and provides real-time
11
+ audio/video streaming capabilities through the Tavus API.
12
+ """
13
+
1
14
  import os
2
15
  from functools import partial
3
16
  from typing import Any, Awaitable, Callable, Mapping, Optional
@@ -7,7 +20,6 @@ from daily.daily import AudioData
7
20
  from loguru import logger
8
21
  from pydantic import BaseModel
9
22
 
10
- from pipecat.audio.utils import create_default_resampler
11
23
  from pipecat.frames.frames import (
12
24
  CancelFrame,
13
25
  EndFrame,
@@ -31,8 +43,10 @@ from pipecat.transports.services.daily import (
31
43
 
32
44
 
33
45
  class TavusApi:
34
- """
35
- A helper class for interacting with the Tavus API (v2).
46
+ """Helper class for interacting with the Tavus API (v2).
47
+
48
+ Provides methods for creating and managing conversations with Tavus avatars,
49
+ including conversation lifecycle management and persona information retrieval.
36
50
  """
37
51
 
38
52
  BASE_URL = "https://tavusapi.com/v2"
@@ -40,12 +54,11 @@ class TavusApi:
40
54
  MOCK_PERSONA_NAME = "TestTavusTransport"
41
55
 
42
56
  def __init__(self, api_key: str, session: aiohttp.ClientSession):
43
- """
44
- Initialize the TavusApi client.
57
+ """Initialize the TavusApi client.
45
58
 
46
59
  Args:
47
- api_key (str): Tavus API key.
48
- session (aiohttp.ClientSession): An aiohttp session for making HTTP requests.
60
+ api_key: Tavus API key for authentication.
61
+ session: An aiohttp session for making HTTP requests.
49
62
  """
50
63
  self._api_key = api_key
51
64
  self._session = session
@@ -54,6 +67,15 @@ class TavusApi:
54
67
  self._dev_room_url = os.getenv("TAVUS_SAMPLE_ROOM_URL")
55
68
 
56
69
  async def create_conversation(self, replica_id: str, persona_id: str) -> dict:
70
+ """Create a new conversation with the specified replica and persona.
71
+
72
+ Args:
73
+ replica_id: ID of the replica to use in the conversation.
74
+ persona_id: ID of the persona to use in the conversation.
75
+
76
+ Returns:
77
+ Dictionary containing conversation_id and conversation_url.
78
+ """
57
79
  if self._dev_room_url:
58
80
  return {
59
81
  "conversation_id": self.MOCK_CONVERSATION_ID,
@@ -73,6 +95,11 @@ class TavusApi:
73
95
  return response
74
96
 
75
97
  async def end_conversation(self, conversation_id: str):
98
+ """End an existing conversation.
99
+
100
+ Args:
101
+ conversation_id: ID of the conversation to end.
102
+ """
76
103
  if conversation_id is None or conversation_id == self.MOCK_CONVERSATION_ID:
77
104
  return
78
105
 
@@ -82,6 +109,14 @@ class TavusApi:
82
109
  logger.debug(f"Ended Tavus conversation {conversation_id}")
83
110
 
84
111
  async def get_persona_name(self, persona_id: str) -> str:
112
+ """Get the name of a persona by ID.
113
+
114
+ Args:
115
+ persona_id: ID of the persona to retrieve.
116
+
117
+ Returns:
118
+ The name of the persona.
119
+ """
85
120
  if self._dev_room_url is not None:
86
121
  return self.MOCK_PERSONA_NAME
87
122
 
@@ -94,11 +129,11 @@ class TavusApi:
94
129
 
95
130
 
96
131
  class TavusCallbacks(BaseModel):
97
- """Callback handlers for the Tavus events.
132
+ """Callback handlers for Tavus events.
98
133
 
99
- Attributes:
100
- on_participant_joined: Called when a participant joins.
101
- on_participant_left: Called when a participant leaves.
134
+ Parameters:
135
+ on_participant_joined: Called when a participant joins the conversation.
136
+ on_participant_left: Called when a participant leaves the conversation.
102
137
  """
103
138
 
104
139
  on_participant_joined: Callable[[Mapping[str, Any]], Awaitable[None]]
@@ -106,7 +141,13 @@ class TavusCallbacks(BaseModel):
106
141
 
107
142
 
108
143
  class TavusParams(DailyParams):
109
- """Configuration parameters for the Tavus transport."""
144
+ """Configuration parameters for the Tavus transport.
145
+
146
+ Parameters:
147
+ audio_in_enabled: Whether to enable audio input from participants.
148
+ audio_out_enabled: Whether to enable audio output to participants.
149
+ microphone_out_enabled: Whether to enable microphone output track.
150
+ """
110
151
 
111
152
  audio_in_enabled: bool = True
112
153
  audio_out_enabled: bool = True
@@ -114,24 +155,14 @@ class TavusParams(DailyParams):
114
155
 
115
156
 
116
157
  class TavusTransportClient:
117
- """
158
+ """Transport client that integrates Pipecat with the Tavus platform.
159
+
118
160
  A transport client that integrates a Pipecat Bot with the Tavus platform by managing
119
161
  conversation sessions using the Tavus API.
120
162
 
121
163
  This client uses `TavusApi` to interact with the Tavus backend services. When a conversation
122
164
  is started via `TavusApi`, Tavus provides a `roomURL` that can be used to connect the Pipecat Bot
123
165
  into the same virtual room where the TavusBot is operating.
124
-
125
- Args:
126
- bot_name (str): The name of the Pipecat bot instance.
127
- params (TavusParams): Optional parameters for Tavus operation. Defaults to `TavusParams()`.
128
- callbacks (TavusCallbacks): Callback handlers for Tavus-related events.
129
- api_key (str): API key for authenticating with Tavus API.
130
- replica_id (str): ID of the replica to use in the Tavus conversation.
131
- persona_id (str): ID of the Tavus persona. Defaults to "pipecat-stream", which signals Tavus to use
132
- the TTS voice of the Pipecat bot instead of a Tavus persona voice.
133
- session (aiohttp.ClientSession): The aiohttp session for making async HTTP requests.
134
- sample_rate: Audio sample rate to be used by the client.
135
166
  """
136
167
 
137
168
  def __init__(
@@ -145,6 +176,19 @@ class TavusTransportClient:
145
176
  persona_id: str = "pipecat-stream",
146
177
  session: aiohttp.ClientSession,
147
178
  ) -> None:
179
+ """Initialize the Tavus transport client.
180
+
181
+ Args:
182
+ bot_name: The name of the Pipecat bot instance.
183
+ params: Optional parameters for Tavus operation.
184
+ callbacks: Callback handlers for Tavus-related events.
185
+ api_key: API key for authenticating with Tavus API.
186
+ replica_id: ID of the replica to use in the Tavus conversation.
187
+ persona_id: ID of the Tavus persona. Defaults to "pipecat-stream",
188
+ which signals Tavus to use the TTS voice of the Pipecat bot
189
+ instead of a Tavus persona voice.
190
+ session: The aiohttp session for making async HTTP requests.
191
+ """
148
192
  self._bot_name = bot_name
149
193
  self._api = TavusApi(api_key, session)
150
194
  self._replica_id = replica_id
@@ -155,11 +199,17 @@ class TavusTransportClient:
155
199
  self._params = params
156
200
 
157
201
  async def _initialize(self) -> str:
202
+ """Initialize the conversation and return the room URL."""
158
203
  response = await self._api.create_conversation(self._replica_id, self._persona_id)
159
204
  self._conversation_id = response["conversation_id"]
160
205
  return response["conversation_url"]
161
206
 
162
207
  async def setup(self, setup: FrameProcessorSetup):
208
+ """Setup the client and initialize the conversation.
209
+
210
+ Args:
211
+ setup: The frame processor setup configuration.
212
+ """
163
213
  if self._conversation_id is not None:
164
214
  logger.debug(f"Conversation ID already defined: {self._conversation_id}")
165
215
  return
@@ -195,6 +245,10 @@ class TavusTransportClient:
195
245
  on_recording_started=partial(self._on_handle_callback, "on_recording_started"),
196
246
  on_recording_stopped=partial(self._on_handle_callback, "on_recording_stopped"),
197
247
  on_recording_error=partial(self._on_handle_callback, "on_recording_error"),
248
+ on_transcription_stopped=partial(
249
+ self._on_handle_callback, "on_transcription_stopped"
250
+ ),
251
+ on_transcription_error=partial(self._on_handle_callback, "on_transcription_error"),
198
252
  )
199
253
  self._client = DailyTransportClient(
200
254
  room_url, None, "Pipecat", self._params, daily_callbacks, self._bot_name
@@ -206,29 +260,44 @@ class TavusTransportClient:
206
260
  self._conversation_id = None
207
261
 
208
262
  async def cleanup(self):
263
+ """Cleanup client resources."""
209
264
  try:
210
265
  await self._client.cleanup()
211
266
  except Exception as e:
212
267
  logger.exception(f"Exception during cleanup: {e}")
213
268
 
214
269
  async def _on_joined(self, data):
270
+ """Handle joined event."""
215
271
  logger.debug("TavusTransportClient joined!")
216
272
 
217
273
  async def _on_left(self):
274
+ """Handle left event."""
218
275
  logger.debug("TavusTransportClient left!")
219
276
 
220
277
  async def _on_handle_callback(self, event_name, *args, **kwargs):
278
+ """Handle generic callback events."""
221
279
  logger.trace(f"[Callback] {event_name} called with args={args}, kwargs={kwargs}")
222
280
 
223
281
  async def get_persona_name(self) -> str:
282
+ """Get the persona name from the API.
283
+
284
+ Returns:
285
+ The name of the current persona.
286
+ """
224
287
  return await self._api.get_persona_name(self._persona_id)
225
288
 
226
289
  async def start(self, frame: StartFrame):
290
+ """Start the client and join the room.
291
+
292
+ Args:
293
+ frame: The start frame containing initialization parameters.
294
+ """
227
295
  logger.debug("TavusTransportClient start invoked!")
228
296
  await self._client.start(frame)
229
297
  await self._client.join()
230
298
 
231
299
  async def stop(self):
300
+ """Stop the client and end the conversation."""
232
301
  await self._client.leave()
233
302
  await self._api.end_conversation(self._conversation_id)
234
303
  self._conversation_id = None
@@ -241,6 +310,15 @@ class TavusTransportClient:
241
310
  video_source: str = "camera",
242
311
  color_format: str = "RGB",
243
312
  ):
313
+ """Capture video from a participant.
314
+
315
+ Args:
316
+ participant_id: ID of the participant to capture video from.
317
+ callback: Callback function to handle video frames.
318
+ framerate: Desired framerate for video capture.
319
+ video_source: Video source to capture from.
320
+ color_format: Color format for video frames.
321
+ """
244
322
  await self._client.capture_participant_video(
245
323
  participant_id, callback, framerate, video_source, color_format
246
324
  )
@@ -253,22 +331,47 @@ class TavusTransportClient:
253
331
  sample_rate: int = 16000,
254
332
  callback_interval_ms: int = 20,
255
333
  ):
334
+ """Capture audio from a participant.
335
+
336
+ Args:
337
+ participant_id: ID of the participant to capture audio from.
338
+ callback: Callback function to handle audio data.
339
+ audio_source: Audio source to capture from.
340
+ sample_rate: Desired sample rate for audio capture.
341
+ callback_interval_ms: Interval between audio callbacks in milliseconds.
342
+ """
256
343
  await self._client.capture_participant_audio(
257
344
  participant_id, callback, audio_source, sample_rate, callback_interval_ms
258
345
  )
259
346
 
260
347
  async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame):
348
+ """Send a message to participants.
349
+
350
+ Args:
351
+ frame: The message frame to send.
352
+ """
261
353
  await self._client.send_message(frame)
262
354
 
263
355
  @property
264
356
  def out_sample_rate(self) -> int:
357
+ """Get the output sample rate.
358
+
359
+ Returns:
360
+ The output sample rate in Hz.
361
+ """
265
362
  return self._client.out_sample_rate
266
363
 
267
364
  @property
268
365
  def in_sample_rate(self) -> int:
366
+ """Get the input sample rate.
367
+
368
+ Returns:
369
+ The input sample rate in Hz.
370
+ """
269
371
  return self._client.in_sample_rate
270
372
 
271
373
  async def send_interrupt_message(self) -> None:
374
+ """Send an interrupt message to the conversation."""
272
375
  transport_frame = TransportMessageUrgentFrame(
273
376
  message={
274
377
  "message_type": "conversation",
@@ -279,6 +382,12 @@ class TavusTransportClient:
279
382
  await self.send_message(transport_frame)
280
383
 
281
384
  async def update_subscriptions(self, participant_settings=None, profile_settings=None):
385
+ """Update subscription settings for participants.
386
+
387
+ Args:
388
+ participant_settings: Per-participant subscription settings.
389
+ profile_settings: Global subscription profile settings.
390
+ """
282
391
  if not self._client:
283
392
  return
284
393
 
@@ -287,11 +396,21 @@ class TavusTransportClient:
287
396
  )
288
397
 
289
398
  async def write_audio_frame(self, frame: OutputAudioRawFrame):
399
+ """Write an audio frame to the transport.
400
+
401
+ Args:
402
+ frame: The audio frame to write.
403
+ """
290
404
  if not self._client:
291
405
  return
292
406
  await self._client.write_audio_frame(frame)
293
407
 
294
408
  async def register_audio_destination(self, destination: str):
409
+ """Register an audio destination for output.
410
+
411
+ Args:
412
+ destination: The destination identifier to register.
413
+ """
295
414
  if not self._client:
296
415
  return
297
416
 
@@ -299,29 +418,51 @@ class TavusTransportClient:
299
418
 
300
419
 
301
420
  class TavusInputTransport(BaseInputTransport):
421
+ """Input transport for receiving audio and events from Tavus conversations.
422
+
423
+ Handles incoming audio streams from participants and manages audio capture
424
+ from the Daily room connected to the Tavus conversation.
425
+ """
426
+
302
427
  def __init__(
303
428
  self,
304
429
  client: TavusTransportClient,
305
430
  params: TransportParams,
306
431
  **kwargs,
307
432
  ):
433
+ """Initialize the Tavus input transport.
434
+
435
+ Args:
436
+ client: The Tavus transport client instance.
437
+ params: Transport configuration parameters.
438
+ **kwargs: Additional arguments passed to parent class.
439
+ """
308
440
  super().__init__(params, **kwargs)
309
441
  self._client = client
310
442
  self._params = params
311
- self._resampler = create_default_resampler()
312
-
313
443
  # Whether we have seen a StartFrame already.
314
444
  self._initialized = False
315
445
 
316
446
  async def setup(self, setup: FrameProcessorSetup):
447
+ """Setup the input transport.
448
+
449
+ Args:
450
+ setup: The frame processor setup configuration.
451
+ """
317
452
  await super().setup(setup)
318
453
  await self._client.setup(setup)
319
454
 
320
455
  async def cleanup(self):
456
+ """Cleanup input transport resources."""
321
457
  await super().cleanup()
322
458
  await self._client.cleanup()
323
459
 
324
460
  async def start(self, frame: StartFrame):
461
+ """Start the input transport.
462
+
463
+ Args:
464
+ frame: The start frame containing initialization parameters.
465
+ """
325
466
  await super().start(frame)
326
467
 
327
468
  if self._initialized:
@@ -333,14 +474,29 @@ class TavusInputTransport(BaseInputTransport):
333
474
  await self.set_transport_ready(frame)
334
475
 
335
476
  async def stop(self, frame: EndFrame):
477
+ """Stop the input transport.
478
+
479
+ Args:
480
+ frame: The end frame signaling transport shutdown.
481
+ """
336
482
  await super().stop(frame)
337
483
  await self._client.stop()
338
484
 
339
485
  async def cancel(self, frame: CancelFrame):
486
+ """Cancel the input transport.
487
+
488
+ Args:
489
+ frame: The cancel frame signaling immediate cancellation.
490
+ """
340
491
  await super().cancel(frame)
341
492
  await self._client.stop()
342
493
 
343
494
  async def start_capturing_audio(self, participant):
495
+ """Start capturing audio from a participant.
496
+
497
+ Args:
498
+ participant: The participant to capture audio from.
499
+ """
344
500
  if self._params.audio_in_enabled:
345
501
  logger.info(
346
502
  f"TavusTransportClient start capturing audio for participant {participant['id']}"
@@ -354,6 +510,7 @@ class TavusInputTransport(BaseInputTransport):
354
510
  async def _on_participant_audio_data(
355
511
  self, participant_id: str, audio: AudioData, audio_source: str
356
512
  ):
513
+ """Handle received participant audio data."""
357
514
  frame = InputAudioRawFrame(
358
515
  audio=audio.audio_frames,
359
516
  sample_rate=audio.audio_frames,
@@ -364,12 +521,25 @@ class TavusInputTransport(BaseInputTransport):
364
521
 
365
522
 
366
523
  class TavusOutputTransport(BaseOutputTransport):
524
+ """Output transport for sending audio and events to Tavus conversations.
525
+
526
+ Handles outgoing audio streams to participants and manages the custom
527
+ audio track expected by the Tavus platform.
528
+ """
529
+
367
530
  def __init__(
368
531
  self,
369
532
  client: TavusTransportClient,
370
533
  params: TransportParams,
371
534
  **kwargs,
372
535
  ):
536
+ """Initialize the Tavus output transport.
537
+
538
+ Args:
539
+ client: The Tavus transport client instance.
540
+ params: Transport configuration parameters.
541
+ **kwargs: Additional arguments passed to parent class.
542
+ """
373
543
  super().__init__(params, **kwargs)
374
544
  self._client = client
375
545
  self._params = params
@@ -380,14 +550,25 @@ class TavusOutputTransport(BaseOutputTransport):
380
550
  self._transport_destination: Optional[str] = "stream"
381
551
 
382
552
  async def setup(self, setup: FrameProcessorSetup):
553
+ """Setup the output transport.
554
+
555
+ Args:
556
+ setup: The frame processor setup configuration.
557
+ """
383
558
  await super().setup(setup)
384
559
  await self._client.setup(setup)
385
560
 
386
561
  async def cleanup(self):
562
+ """Cleanup output transport resources."""
387
563
  await super().cleanup()
388
564
  await self._client.cleanup()
389
565
 
390
566
  async def start(self, frame: StartFrame):
567
+ """Start the output transport.
568
+
569
+ Args:
570
+ frame: The start frame containing initialization parameters.
571
+ """
391
572
  await super().start(frame)
392
573
 
393
574
  if self._initialized:
@@ -403,51 +584,72 @@ class TavusOutputTransport(BaseOutputTransport):
403
584
  await self.set_transport_ready(frame)
404
585
 
405
586
  async def stop(self, frame: EndFrame):
587
+ """Stop the output transport.
588
+
589
+ Args:
590
+ frame: The end frame signaling transport shutdown.
591
+ """
406
592
  await super().stop(frame)
407
593
  await self._client.stop()
408
594
 
409
595
  async def cancel(self, frame: CancelFrame):
596
+ """Cancel the output transport.
597
+
598
+ Args:
599
+ frame: The cancel frame signaling immediate cancellation.
600
+ """
410
601
  await super().cancel(frame)
411
602
  await self._client.stop()
412
603
 
413
604
  async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame):
605
+ """Send a message to participants.
606
+
607
+ Args:
608
+ frame: The message frame to send.
609
+ """
414
610
  logger.info(f"TavusOutputTransport sending message {frame}")
415
611
  await self._client.send_message(frame)
416
612
 
417
613
  async def process_frame(self, frame: Frame, direction: FrameDirection):
614
+ """Process frames and handle interruptions.
615
+
616
+ Args:
617
+ frame: The frame to process.
618
+ direction: The direction of frame flow in the pipeline.
619
+ """
418
620
  await super().process_frame(frame, direction)
419
621
  if isinstance(frame, StartInterruptionFrame):
420
622
  await self._handle_interruptions()
421
623
 
422
624
  async def _handle_interruptions(self):
625
+ """Handle interruption events by sending interrupt message."""
423
626
  await self._client.send_interrupt_message()
424
627
 
425
628
  async def write_audio_frame(self, frame: OutputAudioRawFrame):
629
+ """Write an audio frame to the Tavus transport.
630
+
631
+ Args:
632
+ frame: The audio frame to write.
633
+ """
426
634
  # This is the custom track destination expected by Tavus
427
635
  frame.transport_destination = self._transport_destination
428
636
  await self._client.write_audio_frame(frame)
429
637
 
430
638
  async def register_audio_destination(self, destination: str):
639
+ """Register an audio destination.
640
+
641
+ Args:
642
+ destination: The destination identifier to register.
643
+ """
431
644
  await self._client.register_audio_destination(destination)
432
645
 
433
646
 
434
647
  class TavusTransport(BaseTransport):
435
- """
436
- Transport implementation for Tavus video calls.
648
+ """Transport implementation for Tavus video calls.
437
649
 
438
650
  When used, the Pipecat bot joins the same virtual room as the Tavus Avatar and the user.
439
651
  This is achieved by using `TavusTransportClient`, which initiates the conversation via
440
652
  `TavusApi` and obtains a room URL that all participants connect to.
441
-
442
- Args:
443
- bot_name (str): The name of the Pipecat bot.
444
- session (aiohttp.ClientSession): aiohttp session used for async HTTP requests.
445
- api_key (str): Tavus API key for authentication.
446
- replica_id (str): ID of the replica model used for voice generation.
447
- persona_id (str): ID of the Tavus persona. Defaults to "pipecat-stream" to use the Pipecat TTS voice.
448
- params (TavusParams): Optional Tavus-specific configuration parameters.
449
- input_name (Optional[str]): Optional name for the input transport.
450
- output_name (Optional[str]): Optional name for the output transport.
451
653
  """
452
654
 
453
655
  def __init__(
@@ -461,6 +663,19 @@ class TavusTransport(BaseTransport):
461
663
  input_name: Optional[str] = None,
462
664
  output_name: Optional[str] = None,
463
665
  ):
666
+ """Initialize the Tavus transport.
667
+
668
+ Args:
669
+ bot_name: The name of the Pipecat bot.
670
+ session: aiohttp session used for async HTTP requests.
671
+ api_key: Tavus API key for authentication.
672
+ replica_id: ID of the replica model used for voice generation.
673
+ persona_id: ID of the Tavus persona. Defaults to "pipecat-stream"
674
+ to use the Pipecat TTS voice.
675
+ params: Optional Tavus-specific configuration parameters.
676
+ input_name: Optional name for the input transport.
677
+ output_name: Optional name for the output transport.
678
+ """
464
679
  super().__init__(input_name=input_name, output_name=output_name)
465
680
  self._params = params
466
681
 
@@ -487,11 +702,13 @@ class TavusTransport(BaseTransport):
487
702
  self._register_event_handler("on_client_disconnected")
488
703
 
489
704
  async def _on_participant_left(self, participant, reason):
705
+ """Handle participant left events."""
490
706
  persona_name = await self._client.get_persona_name()
491
707
  if participant.get("info", {}).get("userName", "") != persona_name:
492
708
  await self._on_client_disconnected(participant)
493
709
 
494
710
  async def _on_participant_joined(self, participant):
711
+ """Handle participant joined events."""
495
712
  # get persona, look up persona_name, set this as the bot name to ignore
496
713
  persona_name = await self._client.get_persona_name()
497
714
 
@@ -513,23 +730,41 @@ class TavusTransport(BaseTransport):
513
730
  await self._input.start_capturing_audio(participant)
514
731
 
515
732
  async def update_subscriptions(self, participant_settings=None, profile_settings=None):
733
+ """Update subscription settings for participants.
734
+
735
+ Args:
736
+ participant_settings: Per-participant subscription settings.
737
+ profile_settings: Global subscription profile settings.
738
+ """
516
739
  await self._client.update_subscriptions(
517
740
  participant_settings=participant_settings,
518
741
  profile_settings=profile_settings,
519
742
  )
520
743
 
521
744
  def input(self) -> FrameProcessor:
745
+ """Get the input transport for receiving media and events.
746
+
747
+ Returns:
748
+ The Tavus input transport instance.
749
+ """
522
750
  if not self._input:
523
751
  self._input = TavusInputTransport(client=self._client, params=self._params)
524
752
  return self._input
525
753
 
526
754
  def output(self) -> FrameProcessor:
755
+ """Get the output transport for sending media and events.
756
+
757
+ Returns:
758
+ The Tavus output transport instance.
759
+ """
527
760
  if not self._output:
528
761
  self._output = TavusOutputTransport(client=self._client, params=self._params)
529
762
  return self._output
530
763
 
531
764
  async def _on_client_connected(self, participant: Any):
765
+ """Handle client connected events."""
532
766
  await self._call_event_handler("on_client_connected", participant)
533
767
 
534
768
  async def _on_client_disconnected(self, participant: Any):
769
+ """Handle client disconnected events."""
535
770
  await self._call_event_handler("on_client_disconnected", participant)