61 lines
1.6 KiB
Python
61 lines
1.6 KiB
Python
|
|
from abc import ABC, abstractmethod
|
||
|
|
|
||
|
|
from pydantic import BaseModel
|
||
|
|
|
||
|
|
from yuxi.utils.logging_config import logger
|
||
|
|
|
||
|
|
|
||
|
|
class VoiceSegment(BaseModel):
|
||
|
|
audio_data: bytes
|
||
|
|
sample_rate: int = 16000
|
||
|
|
channels: int = 1
|
||
|
|
encoding: str = "pcm_s16le"
|
||
|
|
duration_ms: int = 0
|
||
|
|
|
||
|
|
|
||
|
|
class STTResult(BaseModel):
|
||
|
|
text: str
|
||
|
|
confidence: float = 0.0
|
||
|
|
language: str | None = None
|
||
|
|
|
||
|
|
|
||
|
|
class STTProvider(ABC):
|
||
|
|
@abstractmethod
|
||
|
|
async def transcribe(self, segment: VoiceSegment) -> STTResult: ...
|
||
|
|
|
||
|
|
|
||
|
|
class TTSProvider(ABC):
|
||
|
|
@abstractmethod
|
||
|
|
async def synthesize(self, text: str, voice: str = "default") -> bytes: ...
|
||
|
|
|
||
|
|
|
||
|
|
class VoicePolicy:
|
||
|
|
def __init__(self):
|
||
|
|
self._stt_provider: STTProvider | None = None
|
||
|
|
self._tts_provider: TTSProvider | None = None
|
||
|
|
|
||
|
|
def configure_stt(self, provider: STTProvider) -> None:
|
||
|
|
self._stt_provider = provider
|
||
|
|
|
||
|
|
def configure_tts(self, provider: TTSProvider) -> None:
|
||
|
|
self._tts_provider = provider
|
||
|
|
|
||
|
|
async def process_voice(self, segment: VoiceSegment) -> str | None:
|
||
|
|
if not self._stt_provider:
|
||
|
|
return None
|
||
|
|
try:
|
||
|
|
result = await self._stt_provider.transcribe(segment)
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"STT transcription failed: {e}")
|
||
|
|
return None
|
||
|
|
return result.text or None
|
||
|
|
|
||
|
|
async def text_to_speech(self, text: str) -> bytes | None:
|
||
|
|
if not self._tts_provider:
|
||
|
|
return None
|
||
|
|
try:
|
||
|
|
return await self._tts_provider.synthesize(text)
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"TTS synthesis failed: {e}")
|
||
|
|
return None
|