[播报文本净化]:完成TTS表情包过滤,包含emoji清理、上下文净化和回归测试
This commit is contained in:
@@ -26,7 +26,7 @@ from owner_voice_pet.events import (
|
||||
PipelineEventBus,
|
||||
)
|
||||
from owner_voice_pet.llm import MockLlmProvider
|
||||
from owner_voice_pet.models import AudioFrame, AudioSegment, PlaybackResult, ReplyDelta, Transcript, TransportHealth
|
||||
from owner_voice_pet.models import AudioFrame, AudioSegment, ErrorCode, PlaybackResult, ReplyDelta, Transcript, TransportHealth
|
||||
from owner_voice_pet.assistant_pipeline import VoiceAssistantPipeline
|
||||
from owner_voice_pet.runtime import build_live_runtime
|
||||
from owner_voice_pet.stt import MetadataSttProvider, SherpaOnnxSttProvider
|
||||
@@ -409,6 +409,64 @@ class LiveRuntimeTests(unittest.TestCase):
|
||||
self.assertEqual(llm.calls[0][-1].content, "第一问")
|
||||
self.assertNotIn("小杰小杰", llm.calls[0][-1].content)
|
||||
|
||||
def test_assistant_reply_sanitizes_tts_text_and_context(self) -> None:
|
||||
frames = [wake_frame(0, 0)]
|
||||
frames.extend(segment_frames(1, 20, partials=["第一问", "第一问"]))
|
||||
transport = MemoryAudioTransport(frames, flush_clears_input=False)
|
||||
stt = QueueSttProvider(["第一问"])
|
||||
llm = QueueLlmProvider([["你好 😊。没问题[捂脸],我来帮你。"]])
|
||||
context = ConversationContext()
|
||||
runtime = VoiceAssistantPipeline(
|
||||
config=AppConfig(llm_api_key="secret", speech_provider="cloud", wake_ack_text=""),
|
||||
transport=transport,
|
||||
wakeword=KeywordWakeWordProvider(),
|
||||
vad_recorder=VadRecorder(EnergyVadProvider(), min_duration_ms=40, end_silence_ms=40),
|
||||
stt=stt,
|
||||
realtime_stt=MetadataSttProvider(),
|
||||
llm=llm,
|
||||
tts=SineTtsProvider(),
|
||||
context=context,
|
||||
reporter=RecordingReporter(),
|
||||
event_bus=PipelineEventBus(),
|
||||
)
|
||||
|
||||
summary = runtime.run(max_turns=1)
|
||||
context_texts = [message.content for message in context.messages()]
|
||||
|
||||
self.assertEqual(summary.completed_turns, 1)
|
||||
self.assertEqual(transport.played_segments[0].metadata["text"], "你好。没问题,我来帮你。")
|
||||
self.assertIn("你好。没问题,我来帮你。", context_texts)
|
||||
self.assertFalse(any("😊" in text or "[捂脸]" in text for text in context_texts))
|
||||
|
||||
def test_emoji_only_reply_recovers_without_tts_playback(self) -> None:
|
||||
frames = [wake_frame(0, 0)]
|
||||
frames.extend(segment_frames(1, 20, partials=["第一问", "第一问"]))
|
||||
transport = MemoryAudioTransport(frames, flush_clears_input=False)
|
||||
runtime = VoiceAssistantPipeline(
|
||||
config=AppConfig(llm_api_key="secret", speech_provider="cloud", wake_ack_text=""),
|
||||
transport=transport,
|
||||
wakeword=KeywordWakeWordProvider(),
|
||||
vad_recorder=VadRecorder(EnergyVadProvider(), min_duration_ms=40, end_silence_ms=40),
|
||||
stt=QueueSttProvider(["第一问"]),
|
||||
realtime_stt=MetadataSttProvider(),
|
||||
llm=QueueLlmProvider([["😂😂"]]),
|
||||
tts=SineTtsProvider(),
|
||||
context=ConversationContext(),
|
||||
reporter=RecordingReporter(),
|
||||
event_bus=PipelineEventBus(),
|
||||
)
|
||||
|
||||
summary = runtime.run(once=True)
|
||||
event_types = [event.type for event in runtime.event_bus.events]
|
||||
|
||||
self.assertEqual(summary.completed_turns, 0)
|
||||
self.assertEqual(summary.failed_turns, 1)
|
||||
self.assertIsNotNone(summary.last_error)
|
||||
self.assertEqual(summary.last_error.code, ErrorCode.TTS_EMPTY_AUDIO)
|
||||
self.assertEqual(transport.played_segments, [])
|
||||
self.assertNotIn(TTS_STARTED, event_types)
|
||||
self.assertEqual(event_types[-1], STANDBY_RESUMED)
|
||||
|
||||
def test_live_runtime_uses_primary_speaker_endpoint_by_default(self) -> None:
|
||||
runtime = build_live_runtime(AppConfig(llm_api_key="secret"))
|
||||
self.assertIsInstance(runtime.vad_recorder, PrimarySpeakerVadRecorder)
|
||||
|
||||
Reference in New Issue
Block a user