[唤醒灵敏度与端点恢复]:完成唤醒提示顺序和Hybrid VAD优化,包含阈值默认值、缓冲时序和测试覆盖

This commit is contained in:
mkbk
2026-06-17 21:00:28 +08:00
parent 44f708a3dc
commit 95e4996434
13 changed files with 145 additions and 29 deletions
+3
View File
@@ -104,6 +104,9 @@ class LiveRuntimeTests(unittest.TestCase):
self.assertEqual(len(transport.played_segments), 4)
self.assertEqual(reporter.transcripts, ["第一问", "第二问"])
self.assertIn("应答中:我在", reporter.statuses)
self.assertLess(reporter.statuses.index("唤醒命中"), reporter.statuses.index("应答中:我在"))
self.assertLess(reporter.statuses.index("应答中:我在"), reporter.statuses.index("请说出问题"))
self.assertLess(reporter.statuses.index("请说出问题"), reporter.statuses.index("录音中:正在听取问题"))
self.assertIn("恢复待机:可继续唤醒", reporter.statuses[-1])
def test_temporary_context_is_sent_to_second_llm_call(self) -> None:
+4 -4
View File
@@ -70,11 +70,11 @@ class ModelsConfigTests(unittest.TestCase):
self.assertEqual(config.llm_api_key, "secret-value")
self.assertEqual(config.llm_model, "test-model")
self.assertEqual(config.wake_provider, "local_kws")
self.assertEqual(config.wake_kws_threshold, 0.25)
self.assertEqual(config.wake_kws_threshold, 0.15)
self.assertEqual(config.wake_kws_score, 1.0)
self.assertEqual(config.wake_ack_text, "我在")
self.assertEqual(config.post_playback_drain_ms, 250)
self.assertEqual(config.vad_provider, "local")
self.assertEqual(config.post_playback_drain_ms, 50)
self.assertEqual(config.vad_provider, "hybrid")
self.assertEqual(config.vad_threshold, 0.5)
self.assertEqual(config.vad_min_duration_ms, 250)
self.assertEqual(config.vad_end_silence_ms, 350)
@@ -98,7 +98,7 @@ class ModelsConfigTests(unittest.TestCase):
errors = config.validate_basic()
self.assertTrue(any("OWNER_WAKE_PROVIDER" in error.message for error in errors))
def test_vad_provider_must_be_local_or_energy(self) -> None:
def test_vad_provider_must_be_hybrid_local_or_energy(self) -> None:
config = AppConfig(vad_provider="invalid")
errors = config.validate_basic()
self.assertTrue(any("OWNER_VAD_PROVIDER" in error.message for error in errors))
+25 -1
View File
@@ -7,7 +7,7 @@ import unittest
from owner_voice_pet.models import AudioFrame, AudioSegment, ErrorCode, ProviderError
from owner_voice_pet.config import AppConfig
from owner_voice_pet.stt import CloudAsrSttProvider, MetadataSttProvider, SherpaOnnxSttProvider, is_valid_transcript_text
from owner_voice_pet.vad import EnergyVadProvider, VadRecorder
from owner_voice_pet.vad import EnergyVadProvider, HybridVadProvider, VadRecorder
from owner_voice_pet.wakeword import (
KeywordWakeWordProvider,
MissingWakeWordModelProvider,
@@ -87,6 +87,30 @@ class WakeVadSttTests(unittest.TestCase):
self.assertIsInstance(result, ProviderError)
self.assertEqual(result.code, ErrorCode.VAD_TIMEOUT_NO_SPEECH)
def test_hybrid_vad_accepts_energy_fallback_speech(self) -> None:
class SilentVadProvider:
def __init__(self) -> None:
self.loaded = False
def load(self) -> None:
self.loaded = True
def analyze(self, frame: AudioFrame):
return type(
"Result",
(),
{"is_speech": False, "confidence": 0.1, "speech_ms": 0, "silence_ms": 20},
)()
def reset(self) -> None:
return None
provider = HybridVadProvider(SilentVadProvider(), EnergyVadProvider())
provider.load()
result = provider.analyze(make_frame(1, 0, speech=True))
self.assertTrue(result.is_speech)
self.assertEqual(result.speech_ms, 20)
def test_metadata_stt_transcribes_fixture_text(self) -> None:
provider = MetadataSttProvider()
provider.load()