Introduce a comprehensive TTS/ASR module that: - Adds /v1/tts-asr/config, /status, /warmup, /tts, /asr endpoints with detailed JSON responses - Implements Apple‑Silicon detection, device selection (MPS/CUDA/CPU), and memory limiting logic - Supports selectable model size, quantization, and offline mode via environment variables - Adds robust audio validation and multi‑path resampling fallback - Provides new README sections for API usage, device detection, and performance benchmarking - Includes a full testing suite: unit tests, integration tests, macOS simulation and performance reports - Updates backend dependencies and CI scripts - Adds new front‑end views and components for Univer editor integration All changes are backward compatible; new features are exposed through environment variables and new API routes.
189 lines
6.7 KiB
Python
189 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
快速验证脚本
|
||
验证TTS/ASR模块修复是否正确应用
|
||
|
||
运行方式:
|
||
python backend/tests/quick_verify.py
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
# 设置控制台编码
|
||
if sys.platform == 'win32':
|
||
import io
|
||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
||
|
||
# 确保可以导入backend模块
|
||
script_path = Path(__file__).resolve()
|
||
project_root = script_path.parent.parent.parent
|
||
sys.path.insert(0, str(project_root))
|
||
|
||
print(f"项目根目录: {project_root}")
|
||
print(f"脚本路径: {script_path}")
|
||
|
||
|
||
def check_file_exists(filepath: str, description: str) -> bool:
|
||
"""检查文件是否存在"""
|
||
full_path = project_root / filepath
|
||
exists = full_path.exists()
|
||
status = "[OK]" if exists else "[FAIL]"
|
||
print(f"{status} {description}: {filepath} (完整路径: {full_path})")
|
||
return exists
|
||
|
||
|
||
def check_function_exists(module_name: str, function_name: str) -> bool:
|
||
"""检查函数是否存在"""
|
||
try:
|
||
module = __import__(module_name, fromlist=[function_name])
|
||
exists = hasattr(module, function_name)
|
||
status = "[OK]" if exists else "[FAIL]"
|
||
print(f"{status} 函数存在: {module_name}.{function_name}")
|
||
return exists
|
||
except Exception as e:
|
||
print(f"[FAIL] 导入失败: {module_name} - {e}")
|
||
return False
|
||
|
||
|
||
def check_environment_variable(var_name: str, expected_default: str) -> bool:
|
||
"""检查环境变量默认值"""
|
||
try:
|
||
# 清除可能存在的环境变量
|
||
original_value = os.environ.get(var_name)
|
||
if var_name in os.environ:
|
||
del os.environ[var_name]
|
||
|
||
# 重新导入模块
|
||
if 'backend.tts_asr' in sys.modules:
|
||
del sys.modules['backend.tts_asr']
|
||
|
||
from backend.tts_asr import (
|
||
TTS_ASR_DEVICE, TTS_ASR_MODEL_SIZE, TTS_ASR_QUANTIZE,
|
||
TTS_ASR_OFFLINE_MODE, TTS_ASR_WARMUP, TTS_ASR_WARMUP_TIMEOUT,
|
||
TTS_ASR_IDLE_TIMEOUT, TTS_ASR_MPS_MEMORY_LIMIT_MB
|
||
)
|
||
|
||
var_map = {
|
||
'TTS_ASR_DEVICE': TTS_ASR_DEVICE,
|
||
'TTS_ASR_MODEL_SIZE': TTS_ASR_MODEL_SIZE,
|
||
'TTS_ASR_QUANTIZE': TTS_ASR_QUANTIZE,
|
||
'TTS_ASR_OFFLINE_MODE': TTS_ASR_OFFLINE_MODE,
|
||
'TTS_ASR_WARMUP': TTS_ASR_WARMUP,
|
||
'TTS_ASR_WARMUP_TIMEOUT': TTS_ASR_WARMUP_TIMEOUT,
|
||
'TTS_ASR_IDLE_TIMEOUT': TTS_ASR_IDLE_TIMEOUT,
|
||
'TTS_ASR_MPS_MEMORY_LIMIT_MB': TTS_ASR_MPS_MEMORY_LIMIT_MB,
|
||
}
|
||
|
||
actual_value = var_map.get(var_name)
|
||
if var_name == 'TTS_ASR_MODEL_SIZE':
|
||
expected = 'auto'
|
||
elif var_name == 'TTS_ASR_QUANTIZE':
|
||
expected = False
|
||
elif var_name == 'TTS_ASR_OFFLINE_MODE':
|
||
expected = False
|
||
elif var_name == 'TTS_ASR_WARMUP':
|
||
expected = True
|
||
elif var_name == 'TTS_ASR_WARMUP_TIMEOUT':
|
||
expected = 120
|
||
elif var_name == 'TTS_ASR_IDLE_TIMEOUT':
|
||
expected = 0
|
||
elif var_name == 'TTS_ASR_MPS_MEMORY_LIMIT_MB':
|
||
expected = 8192
|
||
else:
|
||
expected = expected_default
|
||
|
||
matches = actual_value == expected
|
||
status = "[OK]" if matches else "[FAIL]"
|
||
print(f"{status} 环境变量默认值: {var_name} = {actual_value} (预期: {expected})")
|
||
return matches
|
||
|
||
except Exception as e:
|
||
print(f"[FAIL] 检查环境变量失败: {var_name} - {e}")
|
||
return False
|
||
|
||
|
||
def main():
|
||
print("="*70)
|
||
print("TTS/ASR模块快速验证")
|
||
print("="*70)
|
||
|
||
checks = []
|
||
|
||
# 1. 检查文件
|
||
print("\n[1] 文件检查")
|
||
print("-"*70)
|
||
checks.append(check_file_exists("backend/tts_asr.py", "主模块文件"))
|
||
checks.append(check_file_exists("backend/tests/test_tts_asr_unit.py", "单元测试"))
|
||
checks.append(check_file_exists("backend/tests/test_tts_asr_integration.py", "集成测试"))
|
||
checks.append(check_file_exists("backend/tests/simulate_macos.py", "macOS模拟工具"))
|
||
checks.append(check_file_exists("backend/tests/TESTING_GUIDE.md", "测试指南"))
|
||
checks.append(check_file_exists("backend/TTS_ASR_MACOS_FIX.md", "修复文档"))
|
||
|
||
# 2. 检查核心函数
|
||
print("\n[2] 核心函数检查")
|
||
print("-"*70)
|
||
checks.append(check_function_exists("backend.tts_asr", "_is_apple_silicon"))
|
||
checks.append(check_function_exists("backend.tts_asr", "_detect_device_capabilities"))
|
||
checks.append(check_function_exists("backend.tts_asr", "_get_recommended_model_size"))
|
||
checks.append(check_function_exists("backend.tts_asr", "_validate_audio_data"))
|
||
checks.append(check_function_exists("backend.tts_asr", "_resample_audio_robust"))
|
||
checks.append(check_function_exists("backend.tts_asr", "_check_model_cached"))
|
||
|
||
# 3. 检查数据类
|
||
print("\n[3] 数据类检查")
|
||
print("-"*70)
|
||
checks.append(check_function_exists("backend.tts_asr", "DeviceCapabilities"))
|
||
checks.append(check_function_exists("backend.tts_asr", "ModelStatus"))
|
||
|
||
# 4. 检查环境变量
|
||
print("\n[4] 环境变量默认值检查")
|
||
print("-"*70)
|
||
checks.append(check_environment_variable("TTS_ASR_DEVICE", "auto"))
|
||
checks.append(check_environment_variable("TTS_ASR_MODEL_SIZE", "auto"))
|
||
checks.append(check_environment_variable("TTS_ASR_QUANTIZE", "false"))
|
||
checks.append(check_environment_variable("TTS_ASR_OFFLINE_MODE", "false"))
|
||
|
||
# 5. 检查常量
|
||
print("\n[5] 常量检查")
|
||
print("-"*70)
|
||
try:
|
||
from backend.tts_asr import WHISPER_MODEL_SIZES, APPLE_SILICON_DEFAULT_SIZE
|
||
expected_sizes = ['tiny', 'base', 'small', 'medium', 'large', 'turbo']
|
||
sizes_match = list(WHISPER_MODEL_SIZES.keys()) == expected_sizes
|
||
status = "[OK]" if sizes_match else "[FAIL]"
|
||
print(f"{status} WHISPER_MODEL_SIZES: {list(WHISPER_MODEL_SIZES.keys())}")
|
||
checks.append(sizes_match)
|
||
|
||
size_match = APPLE_SILICON_DEFAULT_SIZE == 'small'
|
||
status = "[OK]" if size_match else "[FAIL]"
|
||
print(f"{status} APPLE_SILICON_DEFAULT_SIZE: {APPLE_SILICON_DEFAULT_SIZE}")
|
||
checks.append(size_match)
|
||
except Exception as e:
|
||
print(f"[FAIL] 常量检查失败: {e}")
|
||
checks.extend([False, False])
|
||
|
||
# 汇总结果
|
||
print("\n" + "="*70)
|
||
print("验证结果")
|
||
print("="*70)
|
||
|
||
total = len(checks)
|
||
passed = sum(checks)
|
||
|
||
print(f"通过: {passed}/{total}")
|
||
|
||
if all(checks):
|
||
print("\n[SUCCESS] 所有验证通过!TTS/ASR模块修复已正确应用。")
|
||
return 0
|
||
else:
|
||
print("\n[FAILED] 部分验证失败,请检查上述错误。")
|
||
return 1
|
||
|
||
|
||
if __name__ == '__main__':
|
||
sys.exit(main())
|