Add turn configuration support for assistants

- Introduce a new `turnConfig` field in `AssistantConfig` and `Assistant` models to manage user interaction settings.
- Implement `TurnConfig`, `BargeInConfig`, `VadConfig`, and `TurnDetectionConfig` schemas to define turn management strategies.
- Update the backend to handle turn configuration in the database and during assistant operations.
- Enhance frontend components with a `TurnConfigEditor` for configuring turn settings, including VAD and barge-in strategies.
- Modify existing pages to integrate turn configuration, improving user experience and interaction capabilities.
This commit is contained in:
Xin Wang
2026-07-12 11:08:19 +08:00
parent 00270a5c01
commit 01c563a3e7
13 changed files with 453 additions and 168 deletions

View File

@@ -135,6 +135,7 @@ async def resolve_runtime_config(
prompt=assistant.prompt or "你是一个有帮助的助手。",
runtimeMode=assistant.runtime_mode, # type: ignore[arg-type]
enableInterrupt=assistant.enable_interrupt,
turnConfig=assistant.turn_config or {},
tools=await _tools_for(session, assistant),
# workflow 图:仅 workflow 类型非空,引擎据此启用图驱动对话
graph=(assistant.graph or {}) if assistant.type == "workflow" else {},

View File

@@ -30,7 +30,6 @@ from services.pipecat.service_factory import (
from pipecat.adapters.schemas.function_schema import FunctionSchema
from pipecat.adapters.schemas.tools_schema import ToolsSchema
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.frames.frames import (
EndFrame,
InputTransportMessageFrame,
@@ -59,15 +58,14 @@ from pipecat.runner.utils import (
maybe_capture_participant_camera,
)
from pipecat.services.llm_service import FunctionCallParams
from pipecat.turns.user_start import (
TranscriptionUserTurnStartStrategy,
VADUserTurnStartStrategy,
)
from pipecat.turns.user_mute.base_user_mute_strategy import BaseUserMuteStrategy
from pipecat.turns.user_mute.function_call_user_mute_strategy import (
FunctionCallUserMuteStrategy,
)
from pipecat.turns.user_turn_strategies import UserTurnStrategies
from services.pipecat.turn_config import (
create_user_turn_strategies,
create_vad_analyzer,
)
from pipecat.utils.time import time_now_iso8601
from pipecat.workers.runner import WorkerRunner
@@ -448,18 +446,14 @@ async def run_pipeline(
user_aggregator = LLMUserAggregator(
context,
params=LLMUserAggregatorParams(
vad_analyzer=SileroVADAnalyzer(),
vad_analyzer=create_vad_analyzer(cfg.turnConfig),
user_mute_strategies=[
FunctionCallUserMuteStrategy(),
CallEndingUserMuteStrategy(lambda: call_end.ending),
],
user_turn_strategies=UserTurnStrategies(
start=[
VADUserTurnStartStrategy(enable_interruptions=cfg.enableInterrupt),
TranscriptionUserTurnStartStrategy(
enable_interruptions=cfg.enableInterrupt
),
]
user_turn_strategies=create_user_turn_strategies(
cfg.turnConfig,
enable_interruptions=cfg.enableInterrupt,
),
),
)

View File

@@ -0,0 +1,89 @@
"""把稳定的产品配置映射为 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.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_analyzer(turn_config: dict[str, Any]) -> SileroVADAnalyzer:
vad = _section(turn_config, "vad", "vad")
return SileroVADAnalyzer(
params=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_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)