import os import io import sys import time import shutil import psutil import asyncio from funasr import AutoModel from config.logger import setup_logging from typing import Optional, Tuple, List from core.providers.asr.utils import lang_tag_filter from core.providers.asr.base import ASRProviderBase from core.providers.asr.dto.dto import InterfaceType TAG = __name__ logger = setup_logging() MAX_RETRIES = 2 RETRY_DELAY = 1 # 重试延迟(秒) # 捕获标准输出 class CaptureOutput: def __enter__(self): self._output = io.StringIO() self._original_stdout = sys.stdout sys.stdout = self._output def __exit__(self, exc_type, exc_value, traceback): sys.stdout = self._original_stdout self.output = self._output.getvalue() self._output.close() # 将捕获到的内容通过 logger 输出 if self.output: logger.bind(tag=TAG).info(self.output.strip()) class ASRProvider(ASRProviderBase): def __init__(self, config: dict, delete_audio_file: bool): super().__init__() # 内存检测,要求大于2G min_mem_bytes = 2 * 1024 * 1024 * 1024 try: total_mem = psutil.virtual_memory().total except RuntimeError as e: logger.bind(tag=TAG).warning(f"获取系统内存信息失败,跳过FunASR内存检测: {e}") else: if total_mem < min_mem_bytes: logger.bind(tag=TAG).error(f"可用内存不足2G,当前仅有 {total_mem / (1024*1024):.2f} MB,可能无法启动FunASR") self.interface_type = InterfaceType.LOCAL self.model_dir = config.get("model_dir") self.output_dir = config.get("output_dir") # 修正配置键名 self.language = config.get("language", "auto") self.delete_audio_file = delete_audio_file # 确保输出目录存在 os.makedirs(self.output_dir, exist_ok=True) with CaptureOutput(): self.model = AutoModel( model=self.model_dir, vad_kwargs={"max_single_segment_time": 30000}, disable_update=True, hub="hf", # device="cuda:0", # 启用GPU加速 ) async def speech_to_text( self, opus_data: List[bytes], session_id: str, artifacts=None ) -> Tuple[Optional[str], Optional[str]]: """语音转文本主处理逻辑""" retry_count = 0 while retry_count < MAX_RETRIES: try: if artifacts is None: return "", None # 语音识别 - 使用线程池避免阻塞事件循环 start_time = time.time() result = await asyncio.to_thread( self.model.generate, input=artifacts.pcm_bytes, cache={}, language=self.language, use_itn=True, batch_size_s=60, ) text = lang_tag_filter(result[0]["text"]) logger.bind(tag=TAG).debug( f"语音识别耗时: {time.time() - start_time:.3f}s | 结果: {text['content']}" ) return text, artifacts.file_path except OSError as e: retry_count += 1 if retry_count >= MAX_RETRIES: logger.bind(tag=TAG).error( f"语音识别失败(已重试{retry_count}次): {e}", exc_info=True ) return "", None logger.bind(tag=TAG).warning( f"语音识别失败,正在重试({retry_count}/{MAX_RETRIES}): {e}" ) time.sleep(RETRY_DELAY) except Exception as e: logger.bind(tag=TAG).error(f"语音识别失败: {e}", exc_info=True) return "", None