From f0d04dde1c27b894c1ba865ed7675c73f0fea2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 30 Mar 2026 14:01:06 -0700 Subject: [PATCH] audio(filters): remove KrispFilter --- .github/workflows/python-compatibility.yaml | 2 +- README.md | 1 - .../foundational/07p-interruptible-krisp.py | 130 -------------- pyproject.toml | 1 - src/pipecat/audio/filters/krisp_filter.py | 162 ------------------ 5 files changed, 1 insertion(+), 295 deletions(-) delete mode 100644 examples/foundational/07p-interruptible-krisp.py delete mode 100644 src/pipecat/audio/filters/krisp_filter.py diff --git a/.github/workflows/python-compatibility.yaml b/.github/workflows/python-compatibility.yaml index 26f58f9dc..d0114f115 100644 --- a/.github/workflows/python-compatibility.yaml +++ b/.github/workflows/python-compatibility.yaml @@ -42,7 +42,7 @@ jobs: - name: Test uv sync with all extras run: | - uv sync --group dev --all-extras --no-extra krisp + uv sync --group dev --all-extras - name: Verify installation run: | diff --git a/README.md b/README.md index ddd4db1dc..9693941f4 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,6 @@ You can get started with Pipecat running on your local machine, then move your a ```bash uv sync --group dev --all-extras \ --no-extra gstreamer \ - --no-extra krisp \ --no-extra local \ ``` diff --git a/examples/foundational/07p-interruptible-krisp.py b/examples/foundational/07p-interruptible-krisp.py deleted file mode 100644 index 90aa076d6..000000000 --- a/examples/foundational/07p-interruptible-krisp.py +++ /dev/null @@ -1,130 +0,0 @@ -# -# Copyright (c) 2024-2026, Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - - -import os - -from dotenv import load_dotenv -from loguru import logger - -from pipecat.audio.filters.krisp_filter import KrispFilter -from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMRunFrame -from pipecat.pipeline.pipeline import Pipeline -from pipecat.pipeline.runner import PipelineRunner -from pipecat.pipeline.task import PipelineParams, PipelineTask -from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import ( - LLMContextAggregatorPair, - LLMUserAggregatorParams, -) -from pipecat.runner.types import RunnerArguments -from pipecat.runner.utils import create_transport -from pipecat.services.deepgram.stt import DeepgramSTTService -from pipecat.services.deepgram.tts import DeepgramTTSService -from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import BaseTransport, TransportParams -from pipecat.transports.daily.transport import DailyParams -from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams - -load_dotenv(override=True) - -# We use lambdas to defer transport parameter creation until the transport -# type is selected at runtime. -transport_params = { - "daily": lambda: DailyParams( - audio_in_enabled=True, - audio_out_enabled=True, - audio_in_filter=KrispFilter(), - ), - "twilio": lambda: FastAPIWebsocketParams( - audio_in_enabled=True, - audio_out_enabled=True, - audio_in_filter=KrispFilter(), - ), - "webrtc": lambda: TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - audio_in_filter=KrispFilter(), - ), -} - - -async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): - logger.info(f"Starting bot") - - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) - - tts = DeepgramTTSService( - api_key=os.getenv("DEEPGRAM_API_KEY"), - settings=DeepgramTTSService.Settings( - voice="aura-helios-en", - ), - ) - - llm = OpenAILLMService( - api_key=os.getenv("OPENAI_API_KEY"), - settings=OpenAILLMService.Settings( - system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", - ), - ) - - context = LLMContext() - user_aggregator, assistant_aggregator = LLMContextAggregatorPair( - context, - user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), - ) - - pipeline = Pipeline( - [ - transport.input(), # Transport user input - stt, # STT - user_aggregator, # User responses - llm, # LLM - tts, # TTS - transport.output(), # Transport bot output - assistant_aggregator, # Assistant spoken responses - ] - ) - - task = PipelineTask( - pipeline, - params=PipelineParams( - enable_metrics=True, - enable_usage_metrics=True, - ), - idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, - ) - - @transport.event_handler("on_client_connected") - async def on_client_connected(transport, client): - logger.info(f"Client connected") - # Kick off the conversation. - context.add_message( - {"role": "developer", "content": "Please introduce yourself to the user."} - ) - await task.queue_frames([LLMRunFrame()]) - - @transport.event_handler("on_client_disconnected") - async def on_client_disconnected(transport, client): - logger.info(f"Client disconnected") - await task.cancel() - - runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) - - await runner.run(task) - - -async def bot(runner_args: RunnerArguments): - """Main bot entry point compatible with Pipecat Cloud.""" - transport = await create_transport(runner_args, transport_params) - await run_bot(transport, runner_args) - - -if __name__ == "__main__": - from pipecat.runner.run import main - - main() diff --git a/pyproject.toml b/pyproject.toml index 8accf7a4c..9eea482eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,7 +80,6 @@ hume = [ "hume>=0.11.2,<1" ] inworld = [] koala = [ "pvkoala~=2.0.3" ] kokoro = [ "kokoro-onnx>=0.5.0,<1", "requests>=2.32.5,<3" ] -krisp = [ "pipecat-ai-krisp~=0.4.0" ] langchain = [ "langchain>=1.2.13,<2", "langchain-community>=0.4.1,<1", "langchain-openai>=1.1.12,<2" ] lemonslice = [ "pipecat-ai[daily]" ] livekit = [ "livekit>=1.0.13,<2", "livekit-api>=1.0.5,<2", "tenacity>=8.2.3,<10.0.0", "pyjwt>=2.12.0,<3" ] diff --git a/src/pipecat/audio/filters/krisp_filter.py b/src/pipecat/audio/filters/krisp_filter.py deleted file mode 100644 index 58c4a2835..000000000 --- a/src/pipecat/audio/filters/krisp_filter.py +++ /dev/null @@ -1,162 +0,0 @@ -# -# Copyright (c) 2024-2026, Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - -"""Krisp noise reduction audio filter for Pipecat. - -This module provides an audio filter implementation using Krisp's noise -reduction technology to suppress background noise in audio streams. -""" - -import os - -import numpy as np -from loguru import logger - -from pipecat.audio.filters.base_audio_filter import BaseAudioFilter -from pipecat.frames.frames import FilterControlFrame, FilterEnableFrame - -try: - from pipecat_ai_krisp.audio.krisp_processor import KrispAudioProcessor -except ModuleNotFoundError as e: - logger.error(f"Exception: {e}") - logger.error("In order to use the Krisp filter, you need to `pip install pipecat-ai[krisp]`.") - raise Exception(f"Missing module: {e}") - - -class KrispProcessorManager: - """Singleton manager for KrispAudioProcessor instances. - - Ensures that only one KrispAudioProcessor instance exists for the entire - program. - """ - - _krisp_instance = None - - @classmethod - def get_processor(cls, sample_rate: int, sample_type: str, channels: int, model_path: str): - """Get or create a KrispAudioProcessor instance. - - Args: - sample_rate: Audio sample rate in Hz. - sample_type: Audio sample type (e.g., "PCM_16"). - channels: Number of audio channels. - model_path: Path to the Krisp model file. - - Returns: - Shared KrispAudioProcessor instance. - """ - if cls._krisp_instance is None: - cls._krisp_instance = KrispAudioProcessor( - sample_rate, sample_type, channels, model_path - ) - return cls._krisp_instance - - -class KrispFilter(BaseAudioFilter): - """Audio filter using Krisp noise reduction technology. - - Provides real-time noise reduction for audio streams using Krisp's - proprietary noise suppression algorithms. Requires a Krisp model file - for operation. - - .. deprecated:: 0.0.94 - The KrispFilter is deprecated and will be removed in a future version. - Use KrispVivaFilter instead. - """ - - def __init__( - self, sample_type: str = "PCM_16", channels: int = 1, model_path: str = None - ) -> None: - """Initialize the Krisp noise reduction filter. - - Args: - sample_type: The audio sample format. Defaults to "PCM_16". - channels: Number of audio channels. Defaults to 1. - model_path: Path to the Krisp model file. If None, uses KRISP_MODEL_PATH - environment variable. - - Raises: - ValueError: If model_path is not provided and KRISP_MODEL_PATH is not set. - """ - super().__init__() - - import warnings - - with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn( - "KrispFilter is deprecated and will be removed in a future version. " - "Use KrispVivaFilter instead.", - DeprecationWarning, - stacklevel=2, - ) - - # Set model path, checking environment if not specified - self._model_path = model_path or os.getenv("KRISP_MODEL_PATH") - if not self._model_path: - logger.error( - "Model path for KrispAudioProcessor is not provided and KRISP_MODEL_PATH is not set." - ) - raise ValueError("Model path for KrispAudioProcessor must be provided.") - - self._sample_type = sample_type - self._channels = channels - self._sample_rate = 0 - self._filtering = True - self._krisp_processor = None - - async def start(self, sample_rate: int): - """Initialize the Krisp processor with the transport's sample rate. - - Args: - sample_rate: The sample rate of the input transport in Hz. - """ - self._sample_rate = sample_rate - self._krisp_processor = KrispProcessorManager.get_processor( - self._sample_rate, self._sample_type, self._channels, self._model_path - ) - - async def stop(self): - """Clean up the Krisp processor when stopping.""" - self._krisp_processor = None - - async def process_frame(self, frame: FilterControlFrame): - """Process control frames to enable/disable filtering. - - Args: - frame: The control frame containing filter commands. - """ - if isinstance(frame, FilterEnableFrame): - self._filtering = frame.enable - - async def filter(self, audio: bytes) -> bytes: - """Apply Krisp noise reduction to audio data. - - Converts audio to float32, applies Krisp noise reduction processing, - and returns the filtered audio clipped to int16 range. - - Args: - audio: Raw audio data as bytes to be filtered. - - Returns: - Noise-reduced audio data as bytes. - """ - if not self._filtering: - return audio - - data = np.frombuffer(audio, dtype=np.int16) - - # Add a small epsilon to avoid division by zero. - epsilon = 1e-10 - data = data.astype(np.float32) + epsilon - - # Process the audio chunk to reduce noise - reduced_noise = self._krisp_processor.process(data) - - # Clip and set processed audio back to frame - audio = np.clip(reduced_noise, -32768, 32767).astype(np.int16).tobytes() - - return audio