From 631463e573a797c565b7bbe36364687601a37d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 9 Feb 2026 11:25:22 -0800 Subject: [PATCH 1/3] Use TurnAnalyzerUserTurnStopStrategy as default stop strategy Change the default user turn stop strategy from TranscriptionUserTurnStopStrategy to TurnAnalyzerUserTurnStopStrategy with LocalSmartTurnAnalyzerV3. Also reduce AUDIO_INPUT_TIMEOUT_SECS from 1.0 to 0.5 and remove its debug log. --- src/pipecat/transports/base_input.py | 4 +--- src/pipecat/turns/user_turn_strategies.py | 7 ++++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pipecat/transports/base_input.py b/src/pipecat/transports/base_input.py index 771fe39e1..77ff61bba 100644 --- a/src/pipecat/transports/base_input.py +++ b/src/pipecat/transports/base_input.py @@ -48,7 +48,7 @@ from pipecat.metrics.metrics import MetricsData from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.transports.base_transport import TransportParams -AUDIO_INPUT_TIMEOUT_SECS = 1.0 +AUDIO_INPUT_TIMEOUT_SECS = 0.5 class BaseInputTransport(FrameProcessor): @@ -449,8 +449,6 @@ class BaseInputTransport(FrameProcessor): if not audio_received: continue - logger.debug(f"{self}: audio not received for more than {AUDIO_INPUT_TIMEOUT_SECS}") - ################################################################### # DEPRECATED. if self._user_speaking: diff --git a/src/pipecat/turns/user_turn_strategies.py b/src/pipecat/turns/user_turn_strategies.py index fe637e0ed..0435f141c 100644 --- a/src/pipecat/turns/user_turn_strategies.py +++ b/src/pipecat/turns/user_turn_strategies.py @@ -9,6 +9,7 @@ from dataclasses import dataclass from typing import List, Optional +from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3 from pipecat.turns.user_start import ( BaseUserTurnStartStrategy, ExternalUserTurnStartStrategy, @@ -18,7 +19,7 @@ from pipecat.turns.user_start import ( from pipecat.turns.user_stop import ( BaseUserTurnStopStrategy, ExternalUserTurnStopStrategy, - SpeechTimeoutUserTurnStopStrategy, + TurnAnalyzerUserTurnStopStrategy, ) @@ -29,7 +30,7 @@ class UserTurnStrategies: If no strategies are specified, the following defaults are used: start: [VADUserTurnStartStrategy, TranscriptionUserTurnStartStrategy] - stop: [SpeechTimeoutUserTurnStopStrategy] + stop: [TurnAnalyzerUserTurnStopStrategy(LocalSmartTurnAnalyzerV3)] Attributes: start: A list of user turn start strategies used to detect when @@ -46,7 +47,7 @@ class UserTurnStrategies: if not self.start: self.start = [VADUserTurnStartStrategy(), TranscriptionUserTurnStartStrategy()] if not self.stop: - self.stop = [SpeechTimeoutUserTurnStopStrategy()] + self.stop = [TurnAnalyzerUserTurnStopStrategy(turn_analyzer=LocalSmartTurnAnalyzerV3())] @dataclass From ca0d2e68c3216b39e45a445836f4821ac016708c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 9 Feb 2026 11:30:25 -0800 Subject: [PATCH 2/3] Add changelog for #3689 --- changelog/3689.changed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/3689.changed.md diff --git a/changelog/3689.changed.md b/changelog/3689.changed.md new file mode 100644 index 000000000..98e7dc2b4 --- /dev/null +++ b/changelog/3689.changed.md @@ -0,0 +1 @@ +- Changed default user turn stop strategy from `TranscriptionUserTurnStopStrategy` to `TurnAnalyzerUserTurnStopStrategy` with `LocalSmartTurnAnalyzerV3`. From 944ac925937a40474ee377f04d98af917fa06322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 9 Feb 2026 11:57:14 -0800 Subject: [PATCH 3/3] Fix test_langchain to use explicit stop strategy The default stop strategy changed to TurnAnalyzerUserTurnStopStrategy, which requires actual audio analysis. Use SpeechTimeoutUserTurnStopStrategy explicitly since this test is not testing turn detection. --- .github/workflows/coverage.yaml | 1 + .github/workflows/tests.yaml | 1 + tests/test_langchain.py | 14 ++++++++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index ae0cb9c57..852611eba 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -39,6 +39,7 @@ jobs: --extra google \ --extra langchain \ --extra livekit \ + --extra local-smart-turn-v3 \ --extra piper \ --extra websocket diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index cb35a169c..54495911b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -43,6 +43,7 @@ jobs: --extra google \ --extra langchain \ --extra livekit \ + --extra local-smart-turn-v3 \ --extra piper \ --extra websocket diff --git a/tests/test_langchain.py b/tests/test_langchain.py index 8ec8905b4..cc5c8f030 100644 --- a/tests/test_langchain.py +++ b/tests/test_langchain.py @@ -24,10 +24,15 @@ from pipecat.frames.frames import ( ) from pipecat.pipeline.pipeline import Pipeline from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) from pipecat.processors.frame_processor import FrameProcessor from pipecat.processors.frameworks.langchain import LangchainProcessor from pipecat.tests.utils import SleepFrame, run_test +from pipecat.turns.user_stop import SpeechTimeoutUserTurnStopStrategy +from pipecat.turns.user_turn_strategies import UserTurnStrategies class TestLangchain(unittest.IsolatedAsyncioTestCase): @@ -65,7 +70,12 @@ class TestLangchain(unittest.IsolatedAsyncioTestCase): self.mock_proc = self.MockProcessor("token_collector") context = LLMContext() - context_aggregator = LLMContextAggregatorPair(context) + context_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams( + user_turn_strategies=UserTurnStrategies(stop=[SpeechTimeoutUserTurnStopStrategy()]) + ), + ) pipeline = Pipeline( [context_aggregator.user(), proc, self.mock_proc, context_aggregator.assistant()]