- Update ConversationRecorder to include source and nodeId metadata in transcripts for better context tracking. - Introduce optional variable handling in DynamicVariableStore, allowing for unset variables to be rendered as empty without raising errors. - Refactor WorkflowBrain to apply turn configurations and manage interaction policies dynamically, improving agent responsiveness. - Implement tests to ensure proper handling of updated session variables and workflow metadata in various scenarios.
129 lines
4.2 KiB
Python
129 lines
4.2 KiB
Python
"""把稳定的产品配置映射为 Pipecat 用户轮次策略。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from pipecat.audio.turn.smart_turn.local_smart_turn_v3 import LocalSmartTurnAnalyzerV3
|
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
|
from pipecat.processors.aggregators.llm_context import LLMContext
|
|
from pipecat.processors.aggregators.llm_response_universal import (
|
|
LLMUserAggregator,
|
|
LLMUserAggregatorParams,
|
|
)
|
|
from pipecat.turns.user_start import (
|
|
TranscriptionUserTurnStartStrategy,
|
|
VADUserTurnStartStrategy,
|
|
)
|
|
from pipecat.turns.user_stop import (
|
|
SpeechTimeoutUserTurnStopStrategy,
|
|
TurnAnalyzerUserTurnStopStrategy,
|
|
)
|
|
from pipecat.turns.user_turn_strategies import UserTurnStrategies
|
|
|
|
|
|
DEFAULT_VAD = {
|
|
"confidence": 0.7,
|
|
"start_secs": 0.2,
|
|
"stop_secs": 0.2,
|
|
"min_volume": 0.6,
|
|
}
|
|
DEFAULT_TURN_DETECTION = {
|
|
"strategy": "silence",
|
|
"silence_timeout_secs": 0.6,
|
|
}
|
|
|
|
|
|
def _section(config: dict[str, Any], snake: str, camel: str) -> dict[str, Any]:
|
|
value = config.get(snake, config.get(camel, {}))
|
|
return value if isinstance(value, dict) else {}
|
|
|
|
|
|
def _value(config: dict[str, Any], snake: str, camel: str, default: Any) -> Any:
|
|
return config.get(snake, config.get(camel, default))
|
|
|
|
|
|
def create_vad_params(turn_config: dict[str, Any]) -> VADParams:
|
|
"""Translate product settings into Pipecat's runtime VAD parameters."""
|
|
vad = _section(turn_config, "vad", "vad")
|
|
return VADParams(
|
|
confidence=float(vad.get("confidence", DEFAULT_VAD["confidence"])),
|
|
start_secs=float(_value(vad, "start_secs", "startSecs", 0.2)),
|
|
stop_secs=float(_value(vad, "stop_secs", "stopSecs", 0.2)),
|
|
min_volume=float(_value(vad, "min_volume", "minVolume", 0.6)),
|
|
)
|
|
|
|
|
|
def create_vad_analyzer(turn_config: dict[str, Any]) -> SileroVADAnalyzer:
|
|
return SileroVADAnalyzer(params=create_vad_params(turn_config))
|
|
|
|
|
|
def create_user_turn_strategies(
|
|
turn_config: dict[str, Any], *, enable_interruptions: bool
|
|
) -> UserTurnStrategies:
|
|
barge_in = _section(turn_config, "barge_in", "bargeIn")
|
|
start = []
|
|
strategy = barge_in.get("strategy", "vad")
|
|
if strategy == "vad":
|
|
start.append(VADUserTurnStartStrategy(enable_interruptions=enable_interruptions))
|
|
else:
|
|
start.append(
|
|
TranscriptionUserTurnStartStrategy(enable_interruptions=enable_interruptions)
|
|
)
|
|
|
|
detection = _section(turn_config, "turn_detection", "turnDetection")
|
|
if detection.get("strategy", DEFAULT_TURN_DETECTION["strategy"]) == "smart_turn":
|
|
stop = [
|
|
TurnAnalyzerUserTurnStopStrategy(
|
|
turn_analyzer=LocalSmartTurnAnalyzerV3(),
|
|
wait_for_transcript=True,
|
|
)
|
|
]
|
|
else:
|
|
stop = [
|
|
SpeechTimeoutUserTurnStopStrategy(
|
|
user_speech_timeout=float(
|
|
_value(
|
|
detection,
|
|
"silence_timeout_secs",
|
|
"silenceTimeoutSecs",
|
|
0.6,
|
|
)
|
|
),
|
|
wait_for_transcript=True,
|
|
)
|
|
]
|
|
return UserTurnStrategies(start=start, stop=stop)
|
|
|
|
|
|
class ConfigurableLLMUserAggregator(LLMUserAggregator):
|
|
"""LLM user aggregator with one stable project-level runtime update API.
|
|
|
|
Pipecat 1.5 exposes ``UserTurnController.update_strategies`` but does not
|
|
surface it on ``LLMUserAggregator``. Keeping that version-specific bridge
|
|
here prevents Workflow orchestration from depending on Pipecat internals.
|
|
VAD threshold updates still use Pipecat's public ``VADParamsUpdateFrame``.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
context: LLMContext,
|
|
*,
|
|
params: LLMUserAggregatorParams | None = None,
|
|
**kwargs: Any,
|
|
) -> None:
|
|
super().__init__(context, params=params, **kwargs)
|
|
|
|
async def apply_turn_strategies(
|
|
self,
|
|
turn_config: dict[str, Any],
|
|
*,
|
|
enable_interruptions: bool,
|
|
) -> None:
|
|
strategies = create_user_turn_strategies(
|
|
turn_config,
|
|
enable_interruptions=enable_interruptions,
|
|
)
|
|
await self._user_turn_controller.update_strategies(strategies)
|