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
pipecat/pipeline/runner.py
CHANGED
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
5
|
#
|
|
6
6
|
|
|
7
|
+
"""Pipeline runner for managing pipeline task execution.
|
|
8
|
+
|
|
9
|
+
This module provides the PipelineRunner class that handles the execution
|
|
10
|
+
of pipeline tasks with signal handling, garbage collection, and lifecycle
|
|
11
|
+
management.
|
|
12
|
+
"""
|
|
13
|
+
|
|
7
14
|
import asyncio
|
|
8
15
|
import gc
|
|
9
16
|
import signal
|
|
@@ -17,14 +24,31 @@ from pipecat.utils.base_object import BaseObject
|
|
|
17
24
|
|
|
18
25
|
|
|
19
26
|
class PipelineRunner(BaseObject):
|
|
27
|
+
"""Manages the execution of pipeline tasks with lifecycle and signal handling.
|
|
28
|
+
|
|
29
|
+
Provides a high-level interface for running pipeline tasks with automatic
|
|
30
|
+
signal handling (SIGINT/SIGTERM), optional garbage collection, and proper
|
|
31
|
+
cleanup of resources.
|
|
32
|
+
"""
|
|
33
|
+
|
|
20
34
|
def __init__(
|
|
21
35
|
self,
|
|
22
36
|
*,
|
|
23
37
|
name: Optional[str] = None,
|
|
24
38
|
handle_sigint: bool = True,
|
|
39
|
+
handle_sigterm: bool = False,
|
|
25
40
|
force_gc: bool = False,
|
|
26
41
|
loop: Optional[asyncio.AbstractEventLoop] = None,
|
|
27
42
|
):
|
|
43
|
+
"""Initialize the pipeline runner.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
name: Optional name for the runner instance.
|
|
47
|
+
handle_sigint: Whether to automatically handle SIGINT signals.
|
|
48
|
+
handle_sigterm: Whether to automatically handle SIGTERM signals.
|
|
49
|
+
force_gc: Whether to force garbage collection after task completion.
|
|
50
|
+
loop: Event loop to use. If None, uses the current running loop.
|
|
51
|
+
"""
|
|
28
52
|
super().__init__(name=name)
|
|
29
53
|
|
|
30
54
|
self._tasks = {}
|
|
@@ -35,11 +59,22 @@ class PipelineRunner(BaseObject):
|
|
|
35
59
|
if handle_sigint:
|
|
36
60
|
self._setup_sigint()
|
|
37
61
|
|
|
62
|
+
if handle_sigterm:
|
|
63
|
+
self._setup_sigterm()
|
|
64
|
+
|
|
38
65
|
async def run(self, task: PipelineTask):
|
|
66
|
+
"""Run a pipeline task to completion.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
task: The pipeline task to execute.
|
|
70
|
+
"""
|
|
39
71
|
logger.debug(f"Runner {self} started running {task}", call_id=task._conversation_id)
|
|
40
72
|
self._tasks[task.name] = task
|
|
41
73
|
params = PipelineTaskParams(loop=self._loop)
|
|
42
|
-
|
|
74
|
+
try:
|
|
75
|
+
await task.run(params)
|
|
76
|
+
except asyncio.CancelledError:
|
|
77
|
+
await self._cancel()
|
|
43
78
|
del self._tasks[task.name]
|
|
44
79
|
|
|
45
80
|
# Cleanup base object.
|
|
@@ -56,27 +91,41 @@ class PipelineRunner(BaseObject):
|
|
|
56
91
|
logger.debug(f"Runner {self} finished running {task}", call_id=task._conversation_id)
|
|
57
92
|
|
|
58
93
|
async def stop_when_done(self):
|
|
94
|
+
"""Schedule all running tasks to stop when their current processing is complete."""
|
|
59
95
|
logger.debug(f"Runner {self} scheduled to stop when all tasks are done")
|
|
60
96
|
await asyncio.gather(*[t.stop_when_done() for t in self._tasks.values()])
|
|
61
97
|
|
|
62
98
|
async def cancel(self):
|
|
99
|
+
"""Cancel all running tasks immediately."""
|
|
63
100
|
logger.debug(f"Cancelling runner {self}")
|
|
101
|
+
await self._cancel()
|
|
102
|
+
|
|
103
|
+
async def _cancel(self):
|
|
104
|
+
"""Cancel all running tasks immediately."""
|
|
64
105
|
await asyncio.gather(*[t.cancel() for t in self._tasks.values()])
|
|
65
106
|
|
|
66
107
|
def _setup_sigint(self):
|
|
108
|
+
"""Set up signal handlers for graceful shutdown."""
|
|
67
109
|
loop = asyncio.get_running_loop()
|
|
68
110
|
loop.add_signal_handler(signal.SIGINT, lambda *args: self._sig_handler())
|
|
111
|
+
|
|
112
|
+
def _setup_sigterm(self):
|
|
113
|
+
"""Set up signal handlers for graceful shutdown."""
|
|
114
|
+
loop = asyncio.get_running_loop()
|
|
69
115
|
loop.add_signal_handler(signal.SIGTERM, lambda *args: self._sig_handler())
|
|
70
116
|
|
|
71
117
|
def _sig_handler(self):
|
|
118
|
+
"""Handle interrupt signals by cancelling all tasks."""
|
|
72
119
|
if not self._sig_task:
|
|
73
120
|
self._sig_task = asyncio.create_task(self._sig_cancel())
|
|
74
121
|
|
|
75
122
|
async def _sig_cancel(self):
|
|
123
|
+
"""Cancel all running tasks due to signal interruption."""
|
|
76
124
|
logger.warning(f"Interruption detected. Cancelling runner {self}")
|
|
77
125
|
await self.cancel()
|
|
78
126
|
|
|
79
127
|
def _gc_collect(self):
|
|
128
|
+
"""Force garbage collection and log results."""
|
|
80
129
|
collected = gc.collect()
|
|
81
130
|
logger.debug(f"Garbage collector: collected {collected} objects.")
|
|
82
131
|
logger.debug(f"Garbage collector: uncollectable objects {gc.garbage}")
|
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
5
|
#
|
|
6
6
|
|
|
7
|
+
"""Synchronous parallel pipeline implementation for concurrent frame processing.
|
|
8
|
+
|
|
9
|
+
This module provides a pipeline that processes frames through multiple parallel
|
|
10
|
+
pipelines simultaneously, synchronizing their output to maintain frame ordering
|
|
11
|
+
and prevent duplicate processing.
|
|
12
|
+
"""
|
|
13
|
+
|
|
7
14
|
import asyncio
|
|
8
15
|
from dataclasses import dataclass
|
|
9
16
|
from itertools import chain
|
|
@@ -15,22 +22,42 @@ from pipecat.frames.frames import ControlFrame, EndFrame, Frame, SystemFrame
|
|
|
15
22
|
from pipecat.pipeline.base_pipeline import BasePipeline
|
|
16
23
|
from pipecat.pipeline.pipeline import Pipeline
|
|
17
24
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor, FrameProcessorSetup
|
|
18
|
-
from pipecat.utils.asyncio.watchdog_queue import WatchdogQueue
|
|
19
25
|
|
|
20
26
|
|
|
21
27
|
@dataclass
|
|
22
28
|
class SyncFrame(ControlFrame):
|
|
23
|
-
"""
|
|
29
|
+
"""Control frame used to synchronize parallel pipeline processing.
|
|
30
|
+
|
|
31
|
+
This frame is sent through parallel pipelines to determine when the
|
|
32
|
+
internal pipelines have finished processing a batch of frames.
|
|
33
|
+
"""
|
|
24
34
|
|
|
25
35
|
pass
|
|
26
36
|
|
|
27
37
|
|
|
28
38
|
class SyncParallelPipelineSource(FrameProcessor):
|
|
39
|
+
"""Source processor for synchronous parallel pipeline processing.
|
|
40
|
+
|
|
41
|
+
Routes frames to parallel pipelines and collects upstream responses
|
|
42
|
+
for synchronization purposes.
|
|
43
|
+
"""
|
|
44
|
+
|
|
29
45
|
def __init__(self, upstream_queue: asyncio.Queue):
|
|
30
|
-
|
|
46
|
+
"""Initialize the sync parallel pipeline source.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
upstream_queue: Queue for collecting upstream frames from the pipeline.
|
|
50
|
+
"""
|
|
51
|
+
super().__init__(enable_direct_mode=True)
|
|
31
52
|
self._up_queue = upstream_queue
|
|
32
53
|
|
|
33
54
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
|
55
|
+
"""Process frames and route them based on direction.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
frame: The frame to process.
|
|
59
|
+
direction: The direction of frame flow.
|
|
60
|
+
"""
|
|
34
61
|
await super().process_frame(frame, direction)
|
|
35
62
|
|
|
36
63
|
match direction:
|
|
@@ -41,11 +68,28 @@ class SyncParallelPipelineSource(FrameProcessor):
|
|
|
41
68
|
|
|
42
69
|
|
|
43
70
|
class SyncParallelPipelineSink(FrameProcessor):
|
|
71
|
+
"""Sink processor for synchronous parallel pipeline processing.
|
|
72
|
+
|
|
73
|
+
Collects downstream frames from parallel pipelines and routes
|
|
74
|
+
upstream frames back through the pipeline.
|
|
75
|
+
"""
|
|
76
|
+
|
|
44
77
|
def __init__(self, downstream_queue: asyncio.Queue):
|
|
45
|
-
|
|
78
|
+
"""Initialize the sync parallel pipeline sink.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
downstream_queue: Queue for collecting downstream frames from the pipeline.
|
|
82
|
+
"""
|
|
83
|
+
super().__init__(enable_direct_mode=True)
|
|
46
84
|
self._down_queue = downstream_queue
|
|
47
85
|
|
|
48
86
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
|
87
|
+
"""Process frames and route them based on direction.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
frame: The frame to process.
|
|
91
|
+
direction: The direction of frame flow.
|
|
92
|
+
"""
|
|
49
93
|
await super().process_frame(frame, direction)
|
|
50
94
|
|
|
51
95
|
match direction:
|
|
@@ -56,36 +100,42 @@ class SyncParallelPipelineSink(FrameProcessor):
|
|
|
56
100
|
|
|
57
101
|
|
|
58
102
|
class SyncParallelPipeline(BasePipeline):
|
|
103
|
+
"""Pipeline that processes frames through multiple parallel pipelines synchronously.
|
|
104
|
+
|
|
105
|
+
Creates multiple parallel processing paths that all receive the same input frames
|
|
106
|
+
and produces synchronized output. Each parallel path is a separate pipeline that
|
|
107
|
+
processes frames independently, with synchronization points to ensure consistent
|
|
108
|
+
ordering and prevent duplicate frame processing.
|
|
109
|
+
|
|
110
|
+
The pipeline uses SyncFrame control frames to coordinate between parallel paths
|
|
111
|
+
and ensure all paths have completed processing before moving to the next frame.
|
|
112
|
+
"""
|
|
113
|
+
|
|
59
114
|
def __init__(self, *args):
|
|
115
|
+
"""Initialize the synchronous parallel pipeline.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
*args: Variable number of processor lists, each representing a parallel pipeline path.
|
|
119
|
+
Each argument should be a list of FrameProcessor instances.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
Exception: If no arguments are provided.
|
|
123
|
+
TypeError: If any argument is not a list of processors.
|
|
124
|
+
"""
|
|
60
125
|
super().__init__()
|
|
61
126
|
|
|
62
127
|
if len(args) == 0:
|
|
63
128
|
raise Exception(f"SyncParallelPipeline needs at least one argument")
|
|
64
129
|
|
|
65
|
-
self._args = args
|
|
66
130
|
self._sinks = []
|
|
67
131
|
self._sources = []
|
|
68
132
|
self._pipelines = []
|
|
69
133
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
def processors_with_metrics(self) -> List[FrameProcessor]:
|
|
75
|
-
return list(chain.from_iterable(p.processors_with_metrics() for p in self._pipelines))
|
|
76
|
-
|
|
77
|
-
#
|
|
78
|
-
# Frame processor
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
async def setup(self, setup: FrameProcessorSetup):
|
|
82
|
-
await super().setup(setup)
|
|
83
|
-
|
|
84
|
-
self._up_queue = WatchdogQueue(setup.task_manager)
|
|
85
|
-
self._down_queue = WatchdogQueue(setup.task_manager)
|
|
134
|
+
self._up_queue = asyncio.Queue()
|
|
135
|
+
self._down_queue = asyncio.Queue()
|
|
86
136
|
|
|
87
137
|
logger.debug(f"Creating {self} pipelines")
|
|
88
|
-
for processors in
|
|
138
|
+
for processors in args:
|
|
89
139
|
if not isinstance(processors, list):
|
|
90
140
|
raise TypeError(f"SyncParallelPipeline argument {processors} is not a list")
|
|
91
141
|
|
|
@@ -95,30 +145,80 @@ class SyncParallelPipeline(BasePipeline):
|
|
|
95
145
|
source = SyncParallelPipelineSource(up_queue)
|
|
96
146
|
sink = SyncParallelPipelineSink(down_queue)
|
|
97
147
|
|
|
98
|
-
# Create pipeline
|
|
99
|
-
pipeline = Pipeline(processors)
|
|
100
|
-
source.link(pipeline)
|
|
101
|
-
pipeline.link(sink)
|
|
102
|
-
self._pipelines.append(pipeline)
|
|
103
|
-
|
|
104
148
|
# Keep track of sources and sinks. We also keep the output queue of
|
|
105
149
|
# the source and the sinks so we can use it later.
|
|
106
150
|
self._sources.append({"processor": source, "queue": down_queue})
|
|
107
151
|
self._sinks.append({"processor": sink, "queue": up_queue})
|
|
108
152
|
|
|
153
|
+
# Create pipeline
|
|
154
|
+
pipeline = Pipeline(processors, source=source, sink=sink)
|
|
155
|
+
self._pipelines.append(pipeline)
|
|
156
|
+
|
|
109
157
|
logger.debug(f"Finished creating {self} pipelines")
|
|
110
158
|
|
|
111
|
-
|
|
159
|
+
#
|
|
160
|
+
# Frame processor
|
|
161
|
+
#
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def processors(self):
|
|
165
|
+
"""Return the list of sub-processors contained within this processor.
|
|
166
|
+
|
|
167
|
+
Only compound processors (e.g. pipelines and parallel pipelines) have
|
|
168
|
+
sub-processors. Non-compound processors will return an empty list.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
The list of sub-processors if this is a compound processor.
|
|
172
|
+
"""
|
|
173
|
+
return self._pipelines
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def entry_processors(self) -> List["FrameProcessor"]:
|
|
177
|
+
"""Return the list of entry processors for this processor.
|
|
178
|
+
|
|
179
|
+
Entry processors are the first processors in a compound processor
|
|
180
|
+
(e.g. pipelines, parallel pipelines). Note that pipelines can also be an
|
|
181
|
+
entry processor as pipelines are processors themselves. Non-compound
|
|
182
|
+
processors will simply return an empty list.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
The list of entry processors.
|
|
186
|
+
"""
|
|
187
|
+
return self._sources
|
|
188
|
+
|
|
189
|
+
def processors_with_metrics(self) -> List[FrameProcessor]:
|
|
190
|
+
"""Collect processors that can generate metrics from all parallel pipelines.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
List of frame processors that support metrics collection from all parallel paths.
|
|
194
|
+
"""
|
|
195
|
+
return list(chain.from_iterable(p.processors_with_metrics() for p in self._pipelines))
|
|
196
|
+
|
|
197
|
+
async def setup(self, setup: FrameProcessorSetup):
|
|
198
|
+
"""Set up the parallel pipeline and all contained processors.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
setup: Configuration for frame processor setup.
|
|
202
|
+
"""
|
|
203
|
+
await super().setup(setup)
|
|
112
204
|
await asyncio.gather(*[p.setup(setup) for p in self._pipelines])
|
|
113
|
-
await asyncio.gather(*[s["processor"].setup(setup) for s in self._sinks])
|
|
114
205
|
|
|
115
206
|
async def cleanup(self):
|
|
207
|
+
"""Clean up the parallel pipeline and all contained processors."""
|
|
116
208
|
await super().cleanup()
|
|
117
|
-
await asyncio.gather(*[s["processor"].cleanup() for s in self._sources])
|
|
118
209
|
await asyncio.gather(*[p.cleanup() for p in self._pipelines])
|
|
119
|
-
await asyncio.gather(*[s["processor"].cleanup() for s in self._sinks])
|
|
120
210
|
|
|
121
211
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
|
212
|
+
"""Process frames through all parallel pipelines with synchronization.
|
|
213
|
+
|
|
214
|
+
Distributes frames to all parallel pipelines and synchronizes their output
|
|
215
|
+
to maintain proper ordering and prevent duplicate processing. Uses SyncFrame
|
|
216
|
+
control frames to coordinate between parallel paths.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
frame: The frame to process.
|
|
220
|
+
direction: The direction of frame flow.
|
|
221
|
+
"""
|
|
122
222
|
await super().process_frame(frame, direction)
|
|
123
223
|
|
|
124
224
|
# The last processor of each pipeline needs to be synchronous otherwise
|