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.
- {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/METADATA +137 -93
- dv_pipecat_ai-0.0.82.dev776.dist-info/RECORD +340 -0
- pipecat/__init__.py +17 -0
- pipecat/adapters/base_llm_adapter.py +36 -1
- pipecat/adapters/schemas/direct_function.py +296 -0
- pipecat/adapters/schemas/function_schema.py +15 -6
- pipecat/adapters/schemas/tools_schema.py +55 -7
- pipecat/adapters/services/anthropic_adapter.py +22 -3
- pipecat/adapters/services/aws_nova_sonic_adapter.py +23 -3
- pipecat/adapters/services/bedrock_adapter.py +22 -3
- pipecat/adapters/services/gemini_adapter.py +16 -3
- pipecat/adapters/services/open_ai_adapter.py +17 -2
- pipecat/adapters/services/open_ai_realtime_adapter.py +23 -3
- pipecat/audio/filters/base_audio_filter.py +30 -6
- pipecat/audio/filters/koala_filter.py +37 -2
- pipecat/audio/filters/krisp_filter.py +59 -6
- pipecat/audio/filters/noisereduce_filter.py +37 -0
- pipecat/audio/interruptions/base_interruption_strategy.py +25 -5
- pipecat/audio/interruptions/min_words_interruption_strategy.py +21 -4
- pipecat/audio/mixers/base_audio_mixer.py +30 -7
- pipecat/audio/mixers/soundfile_mixer.py +53 -6
- pipecat/audio/resamplers/base_audio_resampler.py +17 -9
- pipecat/audio/resamplers/resampy_resampler.py +26 -1
- pipecat/audio/resamplers/soxr_resampler.py +32 -1
- pipecat/audio/resamplers/soxr_stream_resampler.py +101 -0
- pipecat/audio/utils.py +194 -1
- pipecat/audio/vad/silero.py +60 -3
- pipecat/audio/vad/vad_analyzer.py +114 -30
- pipecat/clocks/base_clock.py +19 -0
- pipecat/clocks/system_clock.py +25 -0
- pipecat/extensions/voicemail/__init__.py +0 -0
- pipecat/extensions/voicemail/voicemail_detector.py +707 -0
- pipecat/frames/frames.py +590 -156
- pipecat/metrics/metrics.py +64 -1
- pipecat/observers/base_observer.py +58 -19
- pipecat/observers/loggers/debug_log_observer.py +56 -64
- pipecat/observers/loggers/llm_log_observer.py +8 -1
- pipecat/observers/loggers/transcription_log_observer.py +19 -7
- pipecat/observers/loggers/user_bot_latency_log_observer.py +32 -5
- pipecat/observers/turn_tracking_observer.py +26 -1
- pipecat/pipeline/base_pipeline.py +5 -7
- pipecat/pipeline/base_task.py +52 -9
- pipecat/pipeline/parallel_pipeline.py +121 -177
- pipecat/pipeline/pipeline.py +129 -20
- pipecat/pipeline/runner.py +50 -1
- pipecat/pipeline/sync_parallel_pipeline.py +132 -32
- pipecat/pipeline/task.py +263 -280
- pipecat/pipeline/task_observer.py +85 -34
- pipecat/pipeline/to_be_updated/merge_pipeline.py +32 -2
- pipecat/processors/aggregators/dtmf_aggregator.py +29 -22
- pipecat/processors/aggregators/gated.py +25 -24
- pipecat/processors/aggregators/gated_openai_llm_context.py +22 -2
- pipecat/processors/aggregators/llm_response.py +398 -89
- pipecat/processors/aggregators/openai_llm_context.py +161 -13
- pipecat/processors/aggregators/sentence.py +25 -14
- pipecat/processors/aggregators/user_response.py +28 -3
- pipecat/processors/aggregators/vision_image_frame.py +24 -14
- pipecat/processors/async_generator.py +28 -0
- pipecat/processors/audio/audio_buffer_processor.py +78 -37
- pipecat/processors/consumer_processor.py +25 -6
- pipecat/processors/filters/frame_filter.py +23 -0
- pipecat/processors/filters/function_filter.py +30 -0
- pipecat/processors/filters/identity_filter.py +17 -2
- pipecat/processors/filters/null_filter.py +24 -1
- pipecat/processors/filters/stt_mute_filter.py +56 -21
- pipecat/processors/filters/wake_check_filter.py +46 -3
- pipecat/processors/filters/wake_notifier_filter.py +21 -3
- pipecat/processors/frame_processor.py +488 -131
- pipecat/processors/frameworks/langchain.py +38 -3
- pipecat/processors/frameworks/rtvi.py +719 -34
- pipecat/processors/gstreamer/pipeline_source.py +41 -0
- pipecat/processors/idle_frame_processor.py +26 -3
- pipecat/processors/logger.py +23 -0
- pipecat/processors/metrics/frame_processor_metrics.py +77 -4
- pipecat/processors/metrics/sentry.py +42 -4
- pipecat/processors/producer_processor.py +34 -14
- pipecat/processors/text_transformer.py +22 -10
- pipecat/processors/transcript_processor.py +48 -29
- pipecat/processors/user_idle_processor.py +31 -21
- pipecat/runner/__init__.py +1 -0
- pipecat/runner/daily.py +132 -0
- pipecat/runner/livekit.py +148 -0
- pipecat/runner/run.py +543 -0
- pipecat/runner/types.py +67 -0
- pipecat/runner/utils.py +515 -0
- pipecat/serializers/base_serializer.py +42 -0
- pipecat/serializers/exotel.py +17 -6
- pipecat/serializers/genesys.py +95 -0
- pipecat/serializers/livekit.py +33 -0
- pipecat/serializers/plivo.py +16 -15
- pipecat/serializers/protobuf.py +37 -1
- pipecat/serializers/telnyx.py +18 -17
- pipecat/serializers/twilio.py +32 -16
- pipecat/services/ai_service.py +5 -3
- pipecat/services/anthropic/llm.py +113 -43
- pipecat/services/assemblyai/models.py +63 -5
- pipecat/services/assemblyai/stt.py +64 -11
- pipecat/services/asyncai/__init__.py +0 -0
- pipecat/services/asyncai/tts.py +501 -0
- pipecat/services/aws/llm.py +185 -111
- pipecat/services/aws/stt.py +217 -23
- pipecat/services/aws/tts.py +118 -52
- pipecat/services/aws/utils.py +101 -5
- pipecat/services/aws_nova_sonic/aws.py +82 -64
- pipecat/services/aws_nova_sonic/context.py +15 -6
- pipecat/services/azure/common.py +10 -2
- pipecat/services/azure/image.py +32 -0
- pipecat/services/azure/llm.py +9 -7
- pipecat/services/azure/stt.py +65 -2
- pipecat/services/azure/tts.py +154 -23
- pipecat/services/cartesia/stt.py +125 -8
- pipecat/services/cartesia/tts.py +102 -38
- pipecat/services/cerebras/llm.py +15 -23
- pipecat/services/deepgram/stt.py +19 -11
- pipecat/services/deepgram/tts.py +36 -0
- pipecat/services/deepseek/llm.py +14 -23
- pipecat/services/elevenlabs/tts.py +330 -64
- pipecat/services/fal/image.py +43 -0
- pipecat/services/fal/stt.py +48 -10
- pipecat/services/fireworks/llm.py +14 -21
- pipecat/services/fish/tts.py +109 -9
- pipecat/services/gemini_multimodal_live/__init__.py +1 -0
- pipecat/services/gemini_multimodal_live/events.py +83 -2
- pipecat/services/gemini_multimodal_live/file_api.py +189 -0
- pipecat/services/gemini_multimodal_live/gemini.py +218 -21
- pipecat/services/gladia/config.py +17 -10
- pipecat/services/gladia/stt.py +82 -36
- pipecat/services/google/frames.py +40 -0
- pipecat/services/google/google.py +2 -0
- pipecat/services/google/image.py +39 -2
- pipecat/services/google/llm.py +176 -58
- pipecat/services/google/llm_openai.py +26 -4
- pipecat/services/google/llm_vertex.py +37 -15
- pipecat/services/google/rtvi.py +41 -0
- pipecat/services/google/stt.py +65 -17
- pipecat/services/google/test-google-chirp.py +45 -0
- pipecat/services/google/tts.py +390 -19
- pipecat/services/grok/llm.py +8 -6
- pipecat/services/groq/llm.py +8 -6
- pipecat/services/groq/stt.py +13 -9
- pipecat/services/groq/tts.py +40 -0
- pipecat/services/hamsa/__init__.py +9 -0
- pipecat/services/hamsa/stt.py +241 -0
- pipecat/services/heygen/__init__.py +5 -0
- pipecat/services/heygen/api.py +281 -0
- pipecat/services/heygen/client.py +620 -0
- pipecat/services/heygen/video.py +338 -0
- pipecat/services/image_service.py +5 -3
- pipecat/services/inworld/__init__.py +1 -0
- pipecat/services/inworld/tts.py +592 -0
- pipecat/services/llm_service.py +127 -45
- pipecat/services/lmnt/tts.py +80 -7
- pipecat/services/mcp_service.py +85 -44
- pipecat/services/mem0/memory.py +42 -13
- pipecat/services/minimax/tts.py +74 -15
- pipecat/services/mistral/__init__.py +0 -0
- pipecat/services/mistral/llm.py +185 -0
- pipecat/services/moondream/vision.py +55 -10
- pipecat/services/neuphonic/tts.py +275 -48
- pipecat/services/nim/llm.py +8 -6
- pipecat/services/ollama/llm.py +27 -7
- pipecat/services/openai/base_llm.py +54 -16
- pipecat/services/openai/image.py +30 -0
- pipecat/services/openai/llm.py +7 -5
- pipecat/services/openai/stt.py +13 -9
- pipecat/services/openai/tts.py +42 -10
- pipecat/services/openai_realtime_beta/azure.py +11 -9
- pipecat/services/openai_realtime_beta/context.py +7 -5
- pipecat/services/openai_realtime_beta/events.py +10 -7
- pipecat/services/openai_realtime_beta/openai.py +37 -18
- pipecat/services/openpipe/llm.py +30 -24
- pipecat/services/openrouter/llm.py +9 -7
- pipecat/services/perplexity/llm.py +15 -19
- pipecat/services/piper/tts.py +26 -12
- pipecat/services/playht/tts.py +227 -65
- pipecat/services/qwen/llm.py +8 -6
- pipecat/services/rime/tts.py +128 -17
- pipecat/services/riva/stt.py +160 -22
- pipecat/services/riva/tts.py +67 -2
- pipecat/services/sambanova/llm.py +19 -17
- pipecat/services/sambanova/stt.py +14 -8
- pipecat/services/sarvam/tts.py +60 -13
- pipecat/services/simli/video.py +82 -21
- pipecat/services/soniox/__init__.py +0 -0
- pipecat/services/soniox/stt.py +398 -0
- pipecat/services/speechmatics/stt.py +29 -17
- pipecat/services/stt_service.py +47 -11
- pipecat/services/tavus/video.py +94 -25
- pipecat/services/together/llm.py +8 -6
- pipecat/services/tts_service.py +77 -53
- pipecat/services/ultravox/stt.py +46 -43
- pipecat/services/vision_service.py +5 -3
- pipecat/services/websocket_service.py +12 -11
- pipecat/services/whisper/base_stt.py +58 -12
- pipecat/services/whisper/stt.py +69 -58
- pipecat/services/xtts/tts.py +59 -2
- pipecat/sync/base_notifier.py +19 -0
- pipecat/sync/event_notifier.py +24 -0
- pipecat/tests/utils.py +73 -5
- pipecat/transcriptions/language.py +24 -0
- pipecat/transports/base_input.py +112 -8
- pipecat/transports/base_output.py +235 -13
- pipecat/transports/base_transport.py +119 -0
- pipecat/transports/local/audio.py +76 -0
- pipecat/transports/local/tk.py +84 -0
- pipecat/transports/network/fastapi_websocket.py +174 -15
- pipecat/transports/network/small_webrtc.py +383 -39
- pipecat/transports/network/webrtc_connection.py +214 -8
- pipecat/transports/network/websocket_client.py +171 -1
- pipecat/transports/network/websocket_server.py +147 -9
- pipecat/transports/services/daily.py +792 -70
- pipecat/transports/services/helpers/daily_rest.py +122 -129
- pipecat/transports/services/livekit.py +339 -4
- pipecat/transports/services/tavus.py +273 -38
- pipecat/utils/asyncio/task_manager.py +92 -186
- pipecat/utils/base_object.py +83 -1
- pipecat/utils/network.py +2 -0
- pipecat/utils/string.py +114 -58
- pipecat/utils/text/base_text_aggregator.py +44 -13
- pipecat/utils/text/base_text_filter.py +46 -0
- pipecat/utils/text/markdown_text_filter.py +70 -14
- pipecat/utils/text/pattern_pair_aggregator.py +18 -14
- pipecat/utils/text/simple_text_aggregator.py +43 -2
- pipecat/utils/text/skip_tags_aggregator.py +21 -13
- pipecat/utils/time.py +36 -0
- pipecat/utils/tracing/class_decorators.py +32 -7
- pipecat/utils/tracing/conversation_context_provider.py +12 -2
- pipecat/utils/tracing/service_attributes.py +80 -64
- pipecat/utils/tracing/service_decorators.py +48 -21
- pipecat/utils/tracing/setup.py +13 -7
- pipecat/utils/tracing/turn_context_provider.py +12 -2
- pipecat/utils/tracing/turn_trace_observer.py +27 -0
- pipecat/utils/utils.py +14 -14
- dv_pipecat_ai-0.0.74.dev770.dist-info/RECORD +0 -319
- pipecat/examples/daily_runner.py +0 -64
- pipecat/examples/run.py +0 -265
- pipecat/utils/asyncio/watchdog_async_iterator.py +0 -72
- pipecat/utils/asyncio/watchdog_event.py +0 -42
- pipecat/utils/asyncio/watchdog_priority_queue.py +0 -48
- pipecat/utils/asyncio/watchdog_queue.py +0 -48
- {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/WHEEL +0 -0
- {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/licenses/LICENSE +0 -0
- {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/top_level.txt +0 -0
- /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
|
-
|
|
42
|
+
"""Initialize the task manager with configuration parameters.
|
|
31
43
|
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
Args:
|
|
45
|
+
params: Configuration parameters for task management.
|
|
46
|
+
"""
|
|
34
47
|
pass
|
|
35
48
|
|
|
36
49
|
@abstractmethod
|
|
37
|
-
def
|
|
38
|
-
|
|
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
|
|
54
|
+
The asyncio event loop instance.
|
|
61
55
|
"""
|
|
62
56
|
pass
|
|
63
57
|
|
|
64
58
|
@abstractmethod
|
|
65
|
-
|
|
66
|
-
"""
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
90
|
-
timeout
|
|
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
|
-
|
|
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
|
-
|
|
156
|
-
|
|
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
|
-
|
|
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
|
|
248
|
-
timeout
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
225
|
+
Args:
|
|
226
|
+
task_data: The task metadata.
|
|
227
|
+
"""
|
|
307
228
|
name = task_data.task.get_name()
|
|
308
|
-
|
|
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}")
|
pipecat/utils/base_object.py
CHANGED
|
@@ -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
|
-
|
|
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