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
@@ -4,8 +4,14 @@
4
4
  # SPDX-License-Identifier: BSD 2-Clause License
5
5
  #
6
6
 
7
+ """Asyncio task management.
8
+
9
+ This module provides task management functionality. Includes both abstract base
10
+ classes and concrete implementations for managing asyncio tasks with
11
+ comprehensive monitoring and cleanup capabilities.
12
+ """
13
+
7
14
  import asyncio
8
- import time
9
15
  import traceback
10
16
  from abc import ABC, abstractmethod
11
17
  from dataclasses import dataclass
@@ -13,154 +19,141 @@ from typing import Coroutine, Dict, Optional, Sequence
13
19
 
14
20
  from loguru import logger
15
21
 
16
- WATCHDOG_TIMEOUT = 5.0
17
-
18
22
 
19
23
  @dataclass
20
24
  class TaskManagerParams:
25
+ """Configuration parameters for task manager initialization.
26
+
27
+ Parameters:
28
+ loop: The asyncio event loop to use for task management.
29
+ """
30
+
21
31
  loop: asyncio.AbstractEventLoop
22
- enable_watchdog_timers: bool = False
23
- enable_watchdog_logging: bool = False
24
- watchdog_timeout: float = WATCHDOG_TIMEOUT
25
32
 
26
33
 
27
34
  class BaseTaskManager(ABC):
35
+ """Abstract base class for asyncio task management.
36
+
37
+ Provides the interface for creating, monitoring, and managing asyncio tasks.
38
+ """
39
+
28
40
  @abstractmethod
29
41
  def setup(self, params: TaskManagerParams):
30
- pass
42
+ """Initialize the task manager with configuration parameters.
31
43
 
32
- @abstractmethod
33
- def get_event_loop(self) -> asyncio.AbstractEventLoop:
44
+ Args:
45
+ params: Configuration parameters for task management.
46
+ """
34
47
  pass
35
48
 
36
49
  @abstractmethod
37
- def create_task(
38
- self,
39
- coroutine: Coroutine,
40
- name: str,
41
- *,
42
- enable_watchdog_logging: Optional[bool] = None,
43
- enable_watchdog_timers: Optional[bool] = None,
44
- watchdog_timeout: Optional[float] = None,
45
- ) -> asyncio.Task:
46
- """
47
- Creates and schedules a new asyncio Task that runs the given coroutine.
48
-
49
- The task is added to a global set of created tasks.
50
-
51
- Args:
52
- loop (asyncio.AbstractEventLoop): The event loop to use for creating the task.
53
- coroutine (Coroutine): The coroutine to be executed within the task.
54
- name (str): The name to assign to the task for identification.
55
- enable_watchdog_logging(bool): whether this task should log watchdog processing times.
56
- enable_watchdog_timers(bool): whether this task should have a watchdog timer.
57
- watchdog_timeout(float): watchdog timer timeout for this task.
50
+ def get_event_loop(self) -> asyncio.AbstractEventLoop:
51
+ """Get the event loop used by this task manager.
58
52
 
59
53
  Returns:
60
- asyncio.Task: The created task object.
54
+ The asyncio event loop instance.
61
55
  """
62
56
  pass
63
57
 
64
58
  @abstractmethod
65
- async def wait_for_task(self, task: asyncio.Task, timeout: Optional[float] = None):
66
- """Wait for an asyncio.Task to complete with optional timeout handling.
59
+ def create_task(self, coroutine: Coroutine, name: str) -> asyncio.Task:
60
+ """Creates and schedules a new asyncio Task that runs the given coroutine.
67
61
 
68
- This function awaits the specified asyncio.Task and handles scenarios for
69
- timeouts, cancellations, and other exceptions. It also ensures that the task
70
- is removed from the set of registered tasks upon completion or failure.
62
+ The task is added to a global set of created tasks.
71
63
 
72
64
  Args:
73
- task (asyncio.Task): The asyncio Task to wait for.
74
- timeout (Optional[float], optional): The maximum number of seconds
75
- to wait for the task to complete. If None, waits indefinitely.
76
- Defaults to None.
65
+ coroutine: The coroutine to be executed within the task.
66
+ name: The name to assign to the task for identification.
67
+
68
+ Returns:
69
+ The created task object.
77
70
  """
78
71
  pass
79
72
 
80
73
  @abstractmethod
81
74
  async def cancel_task(self, task: asyncio.Task, timeout: Optional[float] = None):
82
- """Cancels the given asyncio Task and awaits its completion with an
83
- optional timeout.
75
+ """Cancels the given asyncio Task and awaits its completion with an optional timeout.
84
76
 
85
77
  This function removes the task from the set of registered tasks upon
86
78
  completion or failure.
87
79
 
88
80
  Args:
89
- task (asyncio.Task): The task to be cancelled.
90
- timeout (Optional[float]): The optional timeout in seconds to wait for the task to cancel.
91
-
81
+ task: The task to be cancelled.
82
+ timeout: The optional timeout in seconds to wait for the task to cancel.
92
83
  """
93
84
  pass
94
85
 
95
86
  @abstractmethod
96
87
  def current_tasks(self) -> Sequence[asyncio.Task]:
97
- """Returns the list of currently created/registered tasks."""
98
- pass
99
-
100
- @abstractmethod
101
- def task_reset_watchdog(self):
102
- """Resets the running task watchdog timer. If not reset, a warning will
103
- be logged indicating the task is stalling.
88
+ """Returns the list of currently created/registered tasks.
104
89
 
90
+ Returns:
91
+ Sequence of currently managed asyncio tasks.
105
92
  """
106
93
  pass
107
94
 
108
- @property
109
- @abstractmethod
110
- def task_watchdog_enabled(self) -> bool:
111
- """Whether the current running task has a watchdog timer enabled."""
112
- pass
113
-
114
95
 
115
96
  @dataclass
116
97
  class TaskData:
98
+ """Internal data structure for tracking task metadata.
99
+
100
+ Parameters:
101
+ task: The asyncio Task being managed.
102
+ """
103
+
117
104
  task: asyncio.Task
118
- watchdog_timer: asyncio.Event
119
- enable_watchdog_logging: bool
120
- enable_watchdog_timers: bool
121
- watchdog_timeout: float
122
- watchdog_task: Optional[asyncio.Task]
123
105
 
124
106
 
125
107
  class TaskManager(BaseTaskManager):
108
+ """Concrete implementation of BaseTaskManager.
109
+
110
+ Manages asyncio tasks. Provides comprehensive task lifecycle management
111
+ including creation, monitoring, cancellation, and cleanup.
112
+
113
+ """
114
+
126
115
  def __init__(self, conversation_id: Optional[str] = None) -> None:
116
+ """Initialize the task manager with empty task registry."""
127
117
  self._tasks: Dict[str, TaskData] = {}
128
118
  self._params: Optional[TaskManagerParams] = None
129
119
  self._conversation_id = conversation_id
130
120
 
131
121
  def setup(self, params: TaskManagerParams):
122
+ """Initialize the task manager with configuration parameters.
123
+
124
+ Args:
125
+ params: Configuration parameters for task management.
126
+ """
132
127
  if not self._params:
133
128
  self._params = params
134
129
 
135
130
  def get_event_loop(self) -> asyncio.AbstractEventLoop:
131
+ """Get the event loop used by this task manager.
132
+
133
+ Returns:
134
+ The asyncio event loop instance.
135
+
136
+ Raises:
137
+ Exception: If the task manager is not properly set up.
138
+ """
136
139
  if not self._params:
137
140
  raise Exception("TaskManager is not setup: unable to get event loop")
138
141
  return self._params.loop
139
142
 
140
- def create_task(
141
- self,
142
- coroutine: Coroutine,
143
- name: str,
144
- *,
145
- enable_watchdog_logging: Optional[bool] = None,
146
- enable_watchdog_timers: Optional[bool] = None,
147
- watchdog_timeout: Optional[float] = None,
148
- ) -> asyncio.Task:
149
- """
150
- Creates and schedules a new asyncio Task that runs the given coroutine.
143
+ def create_task(self, coroutine: Coroutine, name: str) -> asyncio.Task:
144
+ """Creates and schedules a new asyncio Task that runs the given coroutine.
151
145
 
152
146
  The task is added to a global set of created tasks.
153
147
 
154
148
  Args:
155
- loop (asyncio.AbstractEventLoop): The event loop to use for creating the task.
156
- coroutine (Coroutine): The coroutine to be executed within the task.
157
- name (str): The name to assign to the task for identification.
158
- enable_watchdog_logging(bool): whether this task should log watchdog processing time.
159
- enable_watchdog_timers(bool): whether this task should have a watchdog timer.
160
- watchdog_timeout(float): watchdog timer timeout for this task.
149
+ coroutine: The coroutine to be executed within the task.
150
+ name: The name to assign to the task for identification.
161
151
 
162
152
  Returns:
163
- asyncio.Task: The created task object.
153
+ The created task object.
154
+
155
+ Raises:
156
+ Exception: If the task manager is not properly set up.
164
157
  """
165
158
 
166
159
  async def run_coroutine():
@@ -186,73 +179,23 @@ class TaskManager(BaseTaskManager):
186
179
  task = self._params.loop.create_task(run_coroutine())
187
180
  task.set_name(name)
188
181
  task.add_done_callback(self._task_done_handler)
189
- self._add_task(
190
- TaskData(
191
- task=task,
192
- watchdog_timer=asyncio.Event(),
193
- enable_watchdog_logging=(
194
- enable_watchdog_logging
195
- if enable_watchdog_logging
196
- else self._params.enable_watchdog_logging
197
- ),
198
- enable_watchdog_timers=(
199
- enable_watchdog_timers
200
- if enable_watchdog_timers
201
- else self._params.enable_watchdog_timers
202
- ),
203
- watchdog_timeout=(
204
- watchdog_timeout if watchdog_timeout else self._params.watchdog_timeout
205
- ),
206
- watchdog_task=None,
207
- ),
208
- )
182
+ self._add_task(TaskData(task=task))
209
183
  logger.trace(f"{name}: task created")
210
184
  return task
211
185
 
212
- async def wait_for_task(self, task: asyncio.Task, timeout: Optional[float] = None):
213
- """Wait for an asyncio.Task to complete with optional timeout handling.
214
-
215
- This function awaits the specified asyncio.Task and handles scenarios for
216
- timeouts, cancellations, and other exceptions. It also ensures that the task
217
- is removed from the set of registered tasks upon completion or failure.
218
-
219
- Args:
220
- task (asyncio.Task): The asyncio Task to wait for.
221
- timeout (Optional[float], optional): The maximum number of seconds
222
- to wait for the task to complete. If None, waits indefinitely.
223
- Defaults to None.
224
- """
225
- name = task.get_name()
226
- try:
227
- if timeout:
228
- await asyncio.wait_for(task, timeout=timeout)
229
- else:
230
- await task
231
- except asyncio.TimeoutError:
232
- logger.warning(f"{name}: timed out waiting for task to finish")
233
- except asyncio.CancelledError:
234
- logger.trace(f"{name}: unexpected task cancellation (maybe Ctrl-C?)")
235
- raise
236
- except Exception as e:
237
- logger.exception(f"{name}: unexpected exception while stopping task: {e}")
238
-
239
186
  async def cancel_task(self, task: asyncio.Task, timeout: Optional[float] = None):
240
- """Cancels the given asyncio Task and awaits its completion with an
241
- optional timeout.
187
+ """Cancels the given asyncio Task and awaits its completion with an optional timeout.
242
188
 
243
189
  This function removes the task from the set of registered tasks upon
244
190
  completion or failure.
245
191
 
246
192
  Args:
247
- task (asyncio.Task): The task to be cancelled.
248
- timeout (Optional[float]): The optional timeout in seconds to wait for the task to cancel.
249
-
193
+ task: The task to be cancelled.
194
+ timeout: The optional timeout in seconds to wait for the task to cancel.
250
195
  """
251
196
  name = task.get_name()
252
197
  task.cancel()
253
198
  try:
254
- # Make sure to reset watchdog if a task is cancelled.
255
- self.reset_watchdog(task)
256
199
  if timeout:
257
200
  await asyncio.wait_for(task, timeout=timeout)
258
201
  else:
@@ -268,68 +211,31 @@ class TaskManager(BaseTaskManager):
268
211
  logger.critical(f"{name}: fatal base exception while cancelling task: {e}")
269
212
  raise
270
213
 
271
- def reset_watchdog(self, task: asyncio.Task):
272
- name = task.get_name()
273
- if name in self._tasks and self._tasks[name].enable_watchdog_timers:
274
- self._tasks[name].watchdog_timer.set()
275
-
276
214
  def current_tasks(self) -> Sequence[asyncio.Task]:
277
- """Returns the list of currently created/registered tasks."""
278
- return [data.task for data in self._tasks.values()]
279
-
280
- def task_reset_watchdog(self):
281
- """Resets the running task watchdog timer. If not reset on time, a warning
282
- will be logged indicating the task is stalling.
215
+ """Returns the list of currently created/registered tasks.
283
216
 
217
+ Returns:
218
+ Sequence of currently managed asyncio tasks.
284
219
  """
285
- task = asyncio.current_task()
286
- if task:
287
- self.reset_watchdog(task)
288
-
289
- @property
290
- def task_watchdog_enabled(self) -> bool:
291
- task = asyncio.current_task()
292
- if not task:
293
- return False
294
- name = task.get_name()
295
- return name in self._tasks and self._tasks[name].enable_watchdog_timers
220
+ return [data.task for data in self._tasks.values()]
296
221
 
297
222
  def _add_task(self, task_data: TaskData):
298
- name = task_data.task.get_name()
299
- self._tasks[name] = task_data
300
- if self._params and task_data.enable_watchdog_timers:
301
- watchdog_task = self.get_event_loop().create_task(
302
- self._watchdog_task_handler(task_data)
303
- )
304
- task_data.watchdog_task = watchdog_task
223
+ """Add a task to the internal registry.
305
224
 
306
- async def _watchdog_task_handler(self, task_data: TaskData):
225
+ Args:
226
+ task_data: The task metadata.
227
+ """
307
228
  name = task_data.task.get_name()
308
- timer = task_data.watchdog_timer
309
- enable_watchdog_logging = task_data.enable_watchdog_logging
310
- watchdog_timeout = task_data.watchdog_timeout
311
-
312
- while True:
313
- try:
314
- start_time = time.time()
315
- await asyncio.wait_for(timer.wait(), timeout=watchdog_timeout)
316
- total_time = time.time() - start_time
317
- if enable_watchdog_logging:
318
- logger.debug(f"{name} time between watchdog timer resets: {total_time:.20f}")
319
- except asyncio.TimeoutError:
320
- logger.warning(
321
- f"{name}: task is taking too long {WATCHDOG_TIMEOUT} second(s) (forgot to reset watchdog?)"
322
- )
323
- finally:
324
- timer.clear()
229
+ self._tasks[name] = task_data
325
230
 
326
231
  def _task_done_handler(self, task: asyncio.Task):
232
+ """Handle task completion by removing the task from the registry.
233
+
234
+ Args:
235
+ task: The completed asyncio task.
236
+ """
327
237
  name = task.get_name()
328
238
  try:
329
- task_data = self._tasks[name]
330
- if task_data.watchdog_task:
331
- task_data.watchdog_task.cancel()
332
- task_data.watchdog_task = None
333
239
  del self._tasks[name]
334
240
  except KeyError as e:
335
241
  logger.trace(f"{name}: unable to remove task data (already removed?): {e}")
@@ -4,6 +4,13 @@
4
4
  # SPDX-License-Identifier: BSD 2-Clause License
5
5
  #
6
6
 
7
+ """Base object class providing event handling and lifecycle management.
8
+
9
+ This module provides the foundational BaseObject class that offers common
10
+ functionality including unique identification, naming, event handling,
11
+ and async cleanup for all Pipecat components.
12
+ """
13
+
7
14
  import asyncio
8
15
  import inspect
9
16
  from abc import ABC
@@ -15,7 +22,21 @@ from pipecat.utils.utils import obj_count, obj_id
15
22
 
16
23
 
17
24
  class BaseObject(ABC):
18
- def __init__(self, *, name: Optional[str] = None):
25
+ """Abstract base class providing common functionality for Pipecat objects.
26
+
27
+ Provides unique identification, naming, event handling capabilities,
28
+ and async lifecycle management for all Pipecat components. All major
29
+ classes in the framework should inherit from this base class.
30
+ """
31
+
32
+ def __init__(self, *, name: Optional[str] = None, **kwargs):
33
+ """Initialize the base object.
34
+
35
+ Args:
36
+ name: Optional custom name for the object. If not provided,
37
+ generates a name using the class name and instance count.
38
+ **kwargs: Additional arguments passed to parent class.
39
+ """
19
40
  self._id: int = obj_id()
20
41
  self._name = name or f"{self.__class__.__name__}#{obj_count(self)}"
21
42
 
@@ -29,19 +50,44 @@ class BaseObject(ABC):
29
50
 
30
51
  @property
31
52
  def id(self) -> int:
53
+ """Get the unique identifier for this object.
54
+
55
+ Returns:
56
+ The unique integer ID assigned to this object instance.
57
+ """
32
58
  return self._id
33
59
 
34
60
  @property
35
61
  def name(self) -> str:
62
+ """Get the name of this object.
63
+
64
+ Returns:
65
+ The object's name, either custom-provided or auto-generated.
66
+ """
36
67
  return self._name
37
68
 
38
69
  async def cleanup(self):
70
+ """Clean up resources and wait for running event handlers to complete.
71
+
72
+ This method should be called when the object is no longer needed.
73
+ It waits for all currently executing event handler tasks to finish
74
+ before returning.
75
+ """
39
76
  if self._event_tasks:
40
77
  event_names, tasks = zip(*self._event_tasks)
41
78
  logger.debug(f"{self} waiting on event handlers to finish {list(event_names)}...")
42
79
  await asyncio.wait(tasks)
43
80
 
44
81
  def event_handler(self, event_name: str):
82
+ """Decorator for registering event handlers.
83
+
84
+ Args:
85
+ event_name: The name of the event to handle.
86
+
87
+ Returns:
88
+ The decorator function that registers the handler.
89
+ """
90
+
45
91
  def decorator(handler):
46
92
  self.add_event_handler(event_name, handler)
47
93
  return handler
@@ -49,18 +95,37 @@ class BaseObject(ABC):
49
95
  return decorator
50
96
 
51
97
  def add_event_handler(self, event_name: str, handler):
98
+ """Add an event handler for the specified event.
99
+
100
+ Args:
101
+ event_name: The name of the event to handle.
102
+ handler: The function to call when the event occurs.
103
+ Can be sync or async.
104
+ """
52
105
  if event_name in self._event_handlers:
53
106
  self._event_handlers[event_name].append(handler)
54
107
  else:
55
108
  logger.warning(f"Event handler {event_name} not registered")
56
109
 
57
110
  def _register_event_handler(self, event_name: str):
111
+ """Register an event handler type.
112
+
113
+ Args:
114
+ event_name: The name of the event type to register.
115
+ """
58
116
  if event_name not in self._event_handlers:
59
117
  self._event_handlers[event_name] = []
60
118
  else:
61
119
  logger.warning(f"Event handler {event_name} not registered")
62
120
 
63
121
  async def _call_event_handler(self, event_name: str, *args, **kwargs):
122
+ """Call all registered handlers for the specified event.
123
+
124
+ Args:
125
+ event_name: The name of the event to trigger.
126
+ *args: Positional arguments to pass to event handlers.
127
+ **kwargs: Keyword arguments to pass to event handlers.
128
+ """
64
129
  # If we haven't registered an event handler, we don't need to do
65
130
  # anything.
66
131
  if not self._event_handlers.get(event_name):
@@ -76,6 +141,13 @@ class BaseObject(ABC):
76
141
  task.add_done_callback(self._event_task_finished)
77
142
 
78
143
  async def _run_task(self, event_name: str, *args, **kwargs):
144
+ """Execute all handlers for an event.
145
+
146
+ Args:
147
+ event_name: The name of the event being handled.
148
+ *args: Positional arguments to pass to handlers.
149
+ **kwargs: Keyword arguments to pass to handlers.
150
+ """
79
151
  try:
80
152
  for handler in self._event_handlers[event_name]:
81
153
  if inspect.iscoroutinefunction(handler):
@@ -86,9 +158,19 @@ class BaseObject(ABC):
86
158
  logger.exception(f"Exception in event handler {event_name}: {e}")
87
159
 
88
160
  def _event_task_finished(self, task: asyncio.Task):
161
+ """Clean up completed event handler tasks.
162
+
163
+ Args:
164
+ task: The completed asyncio Task to remove from tracking.
165
+ """
89
166
  tuple_to_remove = next((t for t in self._event_tasks if t[1] == task), None)
90
167
  if tuple_to_remove:
91
168
  self._event_tasks.discard(tuple_to_remove)
92
169
 
93
170
  def __str__(self):
171
+ """Return the string representation of this object.
172
+
173
+ Returns:
174
+ The object's name as its string representation.
175
+ """
94
176
  return self.name
pipecat/utils/network.py CHANGED
@@ -4,6 +4,8 @@
4
4
  # SPDX-License-Identifier: BSD 2-Clause License
5
5
  #
6
6
 
7
+ """Base class for network utilities, providing exponential backoff time calculation."""
8
+
7
9
 
8
10
  def exponential_backoff_time(
9
11
  attempt: int, min_wait: float = 4, max_wait: float = 10, multiplier: float = 1