introduce new BaseTaskManager
This commit is contained in:
@@ -23,7 +23,7 @@ from pipecat.audio.vad.vad_analyzer import VADParams
|
|||||||
from pipecat.clocks.base_clock import BaseClock
|
from pipecat.clocks.base_clock import BaseClock
|
||||||
from pipecat.metrics.metrics import MetricsData
|
from pipecat.metrics.metrics import MetricsData
|
||||||
from pipecat.transcriptions.language import Language
|
from pipecat.transcriptions.language import Language
|
||||||
from pipecat.utils.asyncio import TaskManager
|
from pipecat.utils.asyncio import BaseTaskManager
|
||||||
from pipecat.utils.time import nanoseconds_to_str
|
from pipecat.utils.time import nanoseconds_to_str
|
||||||
from pipecat.utils.utils import obj_count, obj_id
|
from pipecat.utils.utils import obj_count, obj_id
|
||||||
|
|
||||||
@@ -438,7 +438,7 @@ class StartFrame(SystemFrame):
|
|||||||
"""This is the first frame that should be pushed down a pipeline."""
|
"""This is the first frame that should be pushed down a pipeline."""
|
||||||
|
|
||||||
clock: BaseClock
|
clock: BaseClock
|
||||||
task_manager: TaskManager
|
task_manager: BaseTaskManager
|
||||||
audio_in_sample_rate: int = 16000
|
audio_in_sample_rate: int = 16000
|
||||||
audio_out_sample_rate: int = 24000
|
audio_out_sample_rate: int = 24000
|
||||||
allow_interruptions: bool = False
|
allow_interruptions: bool = False
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any, AsyncIterable, Dict, Iterable, List
|
from typing import Any, AsyncIterable, Dict, Iterable, List, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel, ConfigDict
|
from pydantic import BaseModel, ConfigDict
|
||||||
@@ -31,7 +31,7 @@ from pipecat.pipeline.base_pipeline import BasePipeline
|
|||||||
from pipecat.pipeline.base_task import BaseTask
|
from pipecat.pipeline.base_task import BaseTask
|
||||||
from pipecat.pipeline.task_observer import TaskObserver
|
from pipecat.pipeline.task_observer import TaskObserver
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.utils.asyncio import TaskManager
|
from pipecat.utils.asyncio import BaseTaskManager, TaskManager
|
||||||
from pipecat.utils.utils import obj_count, obj_id
|
from pipecat.utils.utils import obj_count, obj_id
|
||||||
|
|
||||||
HEARTBEAT_SECONDS = 1.0
|
HEARTBEAT_SECONDS = 1.0
|
||||||
@@ -134,6 +134,7 @@ class PipelineTask(BaseTask):
|
|||||||
params: PipelineParams = PipelineParams(),
|
params: PipelineParams = PipelineParams(),
|
||||||
observers: List[BaseObserver] = [],
|
observers: List[BaseObserver] = [],
|
||||||
clock: BaseClock = SystemClock(),
|
clock: BaseClock = SystemClock(),
|
||||||
|
task_manager: Optional[BaseTaskManager] = None,
|
||||||
check_dangling_tasks: bool = True,
|
check_dangling_tasks: bool = True,
|
||||||
):
|
):
|
||||||
self._id: int = obj_id()
|
self._id: int = obj_id()
|
||||||
@@ -174,7 +175,7 @@ class PipelineTask(BaseTask):
|
|||||||
self._sink = PipelineTaskSink(self._down_queue)
|
self._sink = PipelineTaskSink(self._down_queue)
|
||||||
pipeline.link(self._sink)
|
pipeline.link(self._sink)
|
||||||
|
|
||||||
self._task_manager = TaskManager()
|
self._task_manager = task_manager or TaskManager()
|
||||||
|
|
||||||
self._observer = TaskObserver(observers=observers, task_manager=self._task_manager)
|
self._observer = TaskObserver(observers=observers, task_manager=self._task_manager)
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from attr import dataclass
|
|||||||
from pipecat.frames.frames import Frame
|
from pipecat.frames.frames import Frame
|
||||||
from pipecat.observers.base_observer import BaseObserver
|
from pipecat.observers.base_observer import BaseObserver
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.utils.asyncio import TaskManager
|
from pipecat.utils.asyncio import BaseTaskManager
|
||||||
from pipecat.utils.utils import obj_count, obj_id
|
from pipecat.utils.utils import obj_count, obj_id
|
||||||
|
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ class TaskObserver(BaseObserver):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *, observers: List[BaseObserver] = [], task_manager: TaskManager):
|
def __init__(self, *, observers: List[BaseObserver] = [], task_manager: BaseTaskManager):
|
||||||
self._id: int = obj_id()
|
self._id: int = obj_id()
|
||||||
self._name: str = f"{self.__class__.__name__}#{obj_count(self)}"
|
self._name: str = f"{self.__class__.__name__}#{obj_count(self)}"
|
||||||
self._observers = observers
|
self._observers = observers
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from pipecat.frames.frames import (
|
|||||||
)
|
)
|
||||||
from pipecat.metrics.metrics import LLMTokenUsage, MetricsData
|
from pipecat.metrics.metrics import LLMTokenUsage, MetricsData
|
||||||
from pipecat.processors.metrics.frame_processor_metrics import FrameProcessorMetrics
|
from pipecat.processors.metrics.frame_processor_metrics import FrameProcessorMetrics
|
||||||
from pipecat.utils.asyncio import TaskManager
|
from pipecat.utils.asyncio import BaseTaskManager
|
||||||
from pipecat.utils.utils import obj_count, obj_id
|
from pipecat.utils.utils import obj_count, obj_id
|
||||||
|
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ class FrameProcessor:
|
|||||||
self._clock: Optional[BaseClock] = None
|
self._clock: Optional[BaseClock] = None
|
||||||
|
|
||||||
# Task Manager
|
# Task Manager
|
||||||
self._task_manager: Optional[TaskManager] = None
|
self._task_manager: Optional[BaseTaskManager] = None
|
||||||
|
|
||||||
# Other properties
|
# Other properties
|
||||||
self._allow_interruptions = False
|
self._allow_interruptions = False
|
||||||
@@ -192,7 +192,7 @@ class FrameProcessor:
|
|||||||
raise Exception(f"{self} Clock is still not initialized.")
|
raise Exception(f"{self} Clock is still not initialized.")
|
||||||
return self._clock
|
return self._clock
|
||||||
|
|
||||||
def get_task_manager(self) -> TaskManager:
|
def get_task_manager(self) -> BaseTaskManager:
|
||||||
if not self._task_manager:
|
if not self._task_manager:
|
||||||
raise Exception(f"{self} TaskManager is still not initialized.")
|
raise Exception(f"{self} TaskManager is still not initialized.")
|
||||||
return self._task_manager
|
return self._task_manager
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from pipecat.serializers.protobuf import ProtobufFrameSerializer
|
|||||||
from pipecat.transports.base_input import BaseInputTransport
|
from pipecat.transports.base_input import BaseInputTransport
|
||||||
from pipecat.transports.base_output import BaseOutputTransport
|
from pipecat.transports.base_output import BaseOutputTransport
|
||||||
from pipecat.transports.base_transport import BaseTransport, TransportParams
|
from pipecat.transports.base_transport import BaseTransport, TransportParams
|
||||||
from pipecat.utils.asyncio import TaskManager
|
from pipecat.utils.asyncio import BaseTaskManager
|
||||||
|
|
||||||
|
|
||||||
class WebsocketClientParams(TransportParams):
|
class WebsocketClientParams(TransportParams):
|
||||||
@@ -57,12 +57,12 @@ class WebsocketClientSession:
|
|||||||
self._callbacks = callbacks
|
self._callbacks = callbacks
|
||||||
self._transport_name = transport_name
|
self._transport_name = transport_name
|
||||||
|
|
||||||
self._task_manager: Optional[TaskManager] = None
|
self._task_manager: Optional[BaseTaskManager] = None
|
||||||
|
|
||||||
self._websocket: Optional[websockets.WebSocketClientProtocol] = None
|
self._websocket: Optional[websockets.WebSocketClientProtocol] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def task_manager(self) -> TaskManager:
|
def task_manager(self) -> BaseTaskManager:
|
||||||
if not self._task_manager:
|
if not self._task_manager:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
f"{self._transport_name}::WebsocketClientSession: TaskManager not initialized (pipeline not started?)"
|
f"{self._transport_name}::WebsocketClientSession: TaskManager not initialized (pipeline not started?)"
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ from pipecat.transcriptions.language import Language
|
|||||||
from pipecat.transports.base_input import BaseInputTransport
|
from pipecat.transports.base_input import BaseInputTransport
|
||||||
from pipecat.transports.base_output import BaseOutputTransport
|
from pipecat.transports.base_output import BaseOutputTransport
|
||||||
from pipecat.transports.base_transport import BaseTransport, TransportParams
|
from pipecat.transports.base_transport import BaseTransport, TransportParams
|
||||||
from pipecat.utils.asyncio import TaskManager
|
from pipecat.utils.asyncio import BaseTaskManager
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from daily import CallClient, Daily, EventHandler
|
from daily import CallClient, Daily, EventHandler
|
||||||
@@ -293,7 +293,7 @@ class DailyTransportClient(EventHandler):
|
|||||||
self._joined_event = asyncio.Event()
|
self._joined_event = asyncio.Event()
|
||||||
self._leave_counter = 0
|
self._leave_counter = 0
|
||||||
|
|
||||||
self._task_manager: Optional[TaskManager] = None
|
self._task_manager: Optional[BaseTaskManager] = None
|
||||||
|
|
||||||
# We use the executor to cleanup the client. We just do it from one
|
# We use the executor to cleanup the client. We just do it from one
|
||||||
# place, so only one thread is really needed.
|
# place, so only one thread is really needed.
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from pipecat.processors.frame_processor import FrameDirection
|
|||||||
from pipecat.transports.base_input import BaseInputTransport
|
from pipecat.transports.base_input import BaseInputTransport
|
||||||
from pipecat.transports.base_output import BaseOutputTransport
|
from pipecat.transports.base_output import BaseOutputTransport
|
||||||
from pipecat.transports.base_transport import BaseTransport, TransportParams
|
from pipecat.transports.base_transport import BaseTransport, TransportParams
|
||||||
from pipecat.utils.asyncio import TaskManager
|
from pipecat.utils.asyncio import BaseTaskManager
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from livekit import rtc
|
from livekit import rtc
|
||||||
@@ -88,7 +88,7 @@ class LiveKitTransportClient:
|
|||||||
self._audio_tracks = {}
|
self._audio_tracks = {}
|
||||||
self._audio_queue = asyncio.Queue()
|
self._audio_queue = asyncio.Queue()
|
||||||
self._other_participant_has_joined = False
|
self._other_participant_has_joined = False
|
||||||
self._task_manager: Optional[TaskManager] = None
|
self._task_manager: Optional[BaseTaskManager] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def participant_id(self) -> str:
|
def participant_id(self) -> str:
|
||||||
|
|||||||
@@ -5,12 +5,76 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
from typing import Coroutine, Optional, Set
|
from typing import Coroutine, Optional, Set
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
class TaskManager:
|
class BaseTaskManager(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def set_event_loop(self, loop: asyncio.AbstractEventLoop):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_event_loop(self) -> asyncio.AbstractEventLoop:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def create_task(self, coroutine: Coroutine, name: str) -> asyncio.Task:
|
||||||
|
"""
|
||||||
|
Creates and schedules a new asyncio Task that runs the given coroutine.
|
||||||
|
|
||||||
|
The task is added to a global set of created tasks.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
loop (asyncio.AbstractEventLoop): The event loop to use for creating the task.
|
||||||
|
coroutine (Coroutine): The coroutine to be executed within the task.
|
||||||
|
name (str): The name to assign to the task for identification.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
asyncio.Task: The created task object.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def wait_for_task(self, task: asyncio.Task, timeout: Optional[float] = None):
|
||||||
|
"""Wait for an asyncio.Task to complete with optional timeout handling.
|
||||||
|
|
||||||
|
This function awaits the specified asyncio.Task and handles scenarios for
|
||||||
|
timeouts, cancellations, and other exceptions. It also ensures that the task
|
||||||
|
is removed from the set of registered tasks upon completion or failure.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task (asyncio.Task): The asyncio Task to wait for.
|
||||||
|
timeout (Optional[float], optional): The maximum number of seconds
|
||||||
|
to wait for the task to complete. If None, waits indefinitely.
|
||||||
|
Defaults to None.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def cancel_task(self, task: asyncio.Task, timeout: Optional[float] = None):
|
||||||
|
"""Cancels the given asyncio Task and awaits its completion with an
|
||||||
|
optional timeout.
|
||||||
|
|
||||||
|
This function removes the task from the set of registered tasks upon
|
||||||
|
completion or failure.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task (asyncio.Task): The task to be cancelled.
|
||||||
|
timeout (Optional[float]): The optional timeout in seconds to wait for the task to cancel.
|
||||||
|
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def current_tasks(self) -> Set[asyncio.Task]:
|
||||||
|
"""Returns the list of currently created/registered tasks."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TaskManager(BaseTaskManager):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._tasks: Set[asyncio.Task] = set()
|
self._tasks: Set[asyncio.Task] = set()
|
||||||
self._loop: Optional[asyncio.AbstractEventLoop] = None
|
self._loop: Optional[asyncio.AbstractEventLoop] = None
|
||||||
|
|||||||
Reference in New Issue
Block a user