ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

一条脚本验证全部语音模型:Voicebox 端到端 TTS 回归测试体系的设计与实现

一条脚本验证全部语音模型:Voicebox 端到端 TTS 回归测试体系的设计与实现 一条脚本验证全部语音模型Voicebox 端到端 TTS 回归测试体系的设计与实现【免费下载链接】voiceboxThe open-source AI voice studio. Clone, dictate, create.项目地址: https://gitcode.com/GitHub_Trending/voicebox1/voicebox本文以 E2E_MODEL_TEST_DESIGN.md 为主体完整讲解 Voicebox 仓库中冻结二进制全模型回归测试的设计目标、10 行测试矩阵、端到端执行流程、超时策略与报告格式并结合 test_all_models_e2e.py 的实际实现与后端路由源码说明每个设计决策在代码中的落点。读完后你能够理解该测试如何在不依赖 pytest、不污染用户环境的前提下对 PyInstaller 打包产物做可复现的 TTS 全模型验证。设计目标与核心约束设计文档明确给出的目标是一个脚本可在 macOS 和 Windows 上运行针对冻结的 PyInstaller 二进制而非开发服务器驱动每一个 TTS 模型记录每个模型的 pass/fail 与错误信息任一模型失败即以非零码退出。生成过程严格串行——同一时刻只加载一个模型。这一目标背后有三条硬约束直接决定了后续所有实现细节被测对象是二进制不是源码。测试必须自己完成找到/构建二进制 → 启动 → 健康检查 → 请求 → 回收进程的全生命周期管理因为开发模式下uvicorn起服务的整套流程不存在于发布产物中。不依赖 pytest。脚本以python -m backend.tests.test_all_models_e2e单命令可调用方便在全新 checkout 上人工触发文档 Non-goals 明确说明当前不在 CI 上运行。不污染用户环境。数据目录使用临时目录模型下载默认走用户已有缓存不碰 HF 缓存。测试矩阵10 行覆盖全部 TTS 引擎矩阵源自 backends/init.py 中的引擎注册与模型配置。每一行对应一次POST /generate调用#enginemodel_sizeprofile kind备注1qwen1.7Bcloned需要参考音频2qwen0.6Bcloned3qwen_custom_voice1.7Bpresetpreset_voice_idRyan4qwen_custom_voice0.6Bpresetpreset_voice_idRyan5luxtts—cloned仅英文6chatterbox—cloned7chatterbox_turbo—cloned仅英文8tada1Bclonedtada-1b仅英文9tada3Bclonedtada-3b-ml多语言10kokoro—presetpreset_voice_idaf_heart所有克隆类引擎第 1、2、5、6、7、8、9 行共享一个用参考 WAV 一次性创建的 profile预设 profile 单独创建两个kokoro 与 qwen_custom_voice。所有 run 的语言统一为en因为英文落在每个引擎支持语言集合的交集中。对照后端源码可以验证矩阵的准确性。backends/init.py 中TTS_ENGINES字典恰好注册了 7 个引擎qwen、qwen_custom_voice、luxtts、chatterbox、chatterbox_turbo、tada、kokoro而 模型配置区 为每个引擎定义了ModelConfig其model_name字段正是脚本中缓存查询使用的键qwen-tts-1.7B/qwen-tts-0.6BL241-L262支持 10 种语言supports_instructFalseqwen-custom-voice-1.7B/qwen-custom-voice-0.6BL269-L288luxtts300 MB仅英文、chatterbox-tts3200 MB24 种语言、chatterbox-turbo1500 MB仅英文、tada-1b4000 MB仅英文、tada-3b-ml8000 MB10 种语言、kokoro350 MB8 种语言这也解释了超时策略中tada-3b-ml 首次下载可达 8 GB的取值依据——它直接来自配置里的size_mb8000。在测试脚本中矩阵以不可变 dataclass 表达比设计文档多携带两个字段label出现在报告里的人类可读名和model_name用于/models/status缓存查询的键见 MATRIX 定义。端到端流程与实现对照设计文档给出的 8 步流程与脚本main()的实际执行一一对应1. Resolve paths → find binary, build if missing 2. Launch binary → spawn with --port --data-dir --parent-pid 3. Wait for /health → poll until statushealthy or 120s timeout 4. Create profiles → 1 cloned 2 preset, via /profiles ( /samples) 5. For each (engine, model_size) in matrix: a. Check cache → GET /models/status → cached? short timeout : long b. POST /generate → get generation_id c. Stream /status → consume SSE until completed/failed/timeout d. Record result → {engine, model_size, status, duration, error, elapsed} 6. Write results → JSON Markdown table to ./results/ 7. Shutdown binary → SIGTERM, fall back to kill, verify port freed 8. Exit code → 0 if all passed, 1 otherwise对照 main() 函数健康检查步骤 3wait_for_health以 1 秒间隔轮询GET /health要求返回 200 且status healthy超时HEALTH_TIMEOUT 120秒期间若检测到服务器进程已退出则提前抛错避免空等到超时L214-L227。Profile 按需创建步骤 4脚本先收集本次要跑的行的profile_kind集合只有集合中包含cloned/preset_kokoro/preset_qwen_cv时才创建对应 profileL538-L558。这意味着--only kokoro这类过滤运行时完全不需要参考音频。报告输出步骤 6write_reports同时生成 JSON 与 Markdown 两个文件L359-L421。退出码步骤 8main()返回0全部通过或1存在失败/超时而配置错误——矩阵被--only/--skip过滤空、找不到二进制、参考音频缺失——直接返回2把环境错误与模型失败区分开。二进制解析与自动构建二进制查找按先命中者胜的顺序执行与 find_binary() 一致平台路径构建类型macOSbackend/dist/voicebox-server-cuda/voicebox-server-cudaonedirCUDAMac 上少见macOSbackend/dist/voicebox-serveronefileCPUWindowsbackend\dist\voicebox-server-cuda\voicebox-server-cuda.exeonedirCUDAWindowsbackend\dist\voicebox-server.exeonefileCPUCUDA 变体优先意味着若机器上已构建过 GPU 版则测的就是 GPU 版——报告中会记录二进制绝对路径与体积binary_size_mb使结果可回溯到具体产物。找不到二进制时脚本调用build_binary()子进程执行 build_binary.py文档估计 5–20 分钟构建失败或构建后仍找不到产物则抛出清晰的RuntimeError--skip-build标志则把行为改为无二进制即报错不触发构建。进程启动、日志采集与看门狗启动命令刻意镜像 Tauri 桌面端拉起 sidecar 的方式。在 tauri/src-tauri/src/main.rs 中Tauri 以--data-dir、--port、--parent-pid三个参数启动后端E2E 脚本采用完全相同的参数面binary --host 127.0.0.1 --port free-port --data-dir tempdir --parent-pid test-pid四个参数的具体实现端口pick_free_port()先bind((127.0.0.1, 0))让内核分配一个空闲端口取回后再关闭、传给被测进程L204-L209避免与本机其他服务冲突。数据目录tempfile.mkdtemp(prefixvoicebox-e2e-)profile 与生成的 WAV 全部落在这里运行结束后删除--keep-data-dir可保留以便排查。父进程 PID传入当前 Python 进程 PID。服务端在 server.py 中实现父进程看门狗父进程消失且宽限期内未收到 disable 请求时服务端自行退出。这保证测试脚本自身崩溃时被测二进制不会变成孤儿进程。日志 teestderr合并进stdoutstderrsubprocess.STDOUT保证单条有序流ServerProcess用一个后台线程把每行同时写入results/server-timestamp.log和内存中的deque(maxlen500)滚动缓冲。某模型失败时缓冲最后 100 行被附在该模型结果记录的server_log_tail字段里随 JSON/Markdown 报告落盘L126-L201。进程回收stop()同样区分平台POSIX 先发SIGTERM等待 10 秒超时再kill()Windows 则直接taskkill /F /T杀整棵进程树——这与设计文档在try/finally中总是杀死被测二进制的安全要求一致main()的finally块保证任何异常路径都会触发清理L610-L616。Profile 准备一个克隆、两个预设所有克隆类引擎共用一个 profile通过两步 HTTP 调用创建POST /profiles { name: e2e-cloned, voice_type: cloned, language: en }然后 multipart 上传参考音频对应 create_cloned_profilePOST /profiles/{id}/samples file: reference WAV reference_text: exact transcription两个预设 profilePOST /profiles { name: e2e-kokoro, voice_type: preset, language: en, preset_engine: kokoro, preset_voice_id: af_heart } POST /profiles { name: e2e-qwen-cv, voice_type: preset, language: en, preset_engine: qwen_custom_voice, preset_voice_id: Ryan }参考音频有明确的质量要求见 fixtures/README.mdreference_voice.wav应为干净语音样本单声道、16–24 kHz、约 5–15 秒reference_voice.txt为逐字精确的转写。由于 fixtures 目录默认未被 gitignore文档提醒若参考音频含个人声音应自行加入本地忽略规则。脚本侧resolve_reference()对缺失文件给出带修复提示的报错放文件到何处、或传--reference-wav/--reference-text见 L463-L483。生成请求与 SSE 状态循环每一行矩阵发一次生成请求POST /generate { profile_id: appropriate profile, text: The quick brown fox jumps over the lazy dog., language: en, engine: engine, model_size: size or omitted, seed: 42, normalize: true }其中model_size仅在row.model_size非空时放入 body与后端 engine_has_model_sizes 的语义一致目前只有 qwen 系列引擎存在多尺寸。固定测试文本、固定seed42、normalizetrue保证了跨 run 参数可比。响应中的id进入 SSE 状态循环GET /generate/{id}/status。该端点在 routes/generations.py 中实现为一个每秒轮询数据库、text/event-stream推送的StreamingResponsepayload 包含id、status、duration、error、source字段状态进入completed/failed后服务端主动结束流。客户端侧的 run_one_generation() 用client.stream逐行消费只解析data:前缀的 JSON 行处理三种终止条件status not_found→ 立即判定failed生成记录不存在status in (completed, failed)→ 返回对应终态与最后一个 payload超过 deadline → 返回timeout与最后收到的 payload。SSE 读取超时设为remaining 5秒外层再以墙钟 deadline 兜底即使流挂住也能在指定时刻脱身。completed后脚本还会调GET /history/{generation_id}拿audio_path相对路径相对data_dir解析校验 WAV 非空audio_bytes 0会被改判为failed并追加 (audio file is empty) 错误L583-L591。这正是通过 端点返回 completed 且产出非空 WAV判定的代码落点。双超时策略按缓存状态分流生成前先用GET /models/status查询目标模型是否已缓存get_model_cached按model_name匹配downloaded字段是否已缓存单模型超时理由是3 分钟默认 180s仅推理对 CPU 构建已很宽裕否20 分钟默认 1200s首次 HF 下载最大可达 8 GBtada-3b-ml两者均可用--timeout-cached/--timeout-download覆盖。关键策略是超时不中断整个 run超时的行标记为timeout后继续下一行最终由聚合结果决定退出码。这保证一次跑完能拿到全矩阵的健康快照而不是被第一个慢模型卡死。结果格式与报告报告写入./results/默认backend/tests/results/gitignored文件名为e2e-platform-arch-timestamp.json与同名.mdplatform 形如darwin-arm64。JSON 结构见 write_reports{ platform: darwin-arm64, binary: /abs/path/voicebox-server, binary_size_mb: 612, started_at: 2026-04-16T12:34:56Z, finished_at: ..., results: [ { engine: qwen, model_size: 1.7B, status: passed|failed|timeout, generation_id: ..., was_cached: true, elapsed_seconds: 12.4, audio_duration: 3.1, audio_path: /tmp/.../gen.wav, error: null, server_log_tail: null } ] }实际实现比设计文档多记录了http_status、audio_bytes、elapsed_seconds整体耗时与label字段server_log_tail仅在非通过行填充最近 100 行服务端日志。伴生的 Markdown 报告是一张汇总表Model / Status / Cached / Elapsed / Audio / Error并对每个失败行追加独立小节内含完整 error 文本与折叠的details服务端日志尾L383-L418。示例| Model | Status | Cached | Elapsed | Audio | Error | |-------|--------|--------|---------|-------|-------| | qwen 1.7B | PASS | yes | 12.4s | 3.10s | | | qwen 0.6B | FAIL | yes | 4.1s | — | CUDA OOM: ... |CLI 参数实际实现的参数面parse_args与设计文档一致python -m backend.tests.test_all_models_e2e [flags] --binary PATH 使用指定二进制跳过自动检测 --skip-build 找不到二进制时直接报错不自动构建 --reference-wav PATH 参考音频默认: backend/tests/fixtures/reference_voice.wav --reference-text STR 参考转写默认: 从 fixtures/reference_voice.txt 读取 --only ENGINE[,...] 只跑指定引擎如 kokoro,qwen --skip ENGINE[,...] 跳过指定引擎 --keep-data-dir 运行后保留临时数据目录 --timeout-cached SEC 覆盖默认 180 秒 --timeout-download SEC 覆盖默认 1200 秒 --port N 覆盖自动挑选的端口 --output-dir PATH 默认: backend/tests/results/--only/--skip由filter_matrix()按引擎名而非行号过滤两者把矩阵滤空时以退出码 2 结束防止看似跑完、实际什么都没测的假绿。文件布局与依赖约束backend/tests/ ├── E2E_MODEL_TEST_DESIGN.md 设计文档本文主体 ├── test_all_models_e2e.py 主脚本当前实现约 630 行 ├── fixtures/ │ ├── reference_voice.wav 用户提供约 5–15 秒干净语音 │ └── reference_voice.txt 逐字转写 └── results/ gitignored ├── e2e-darwin-arm64-ts.json ├── e2e-darwin-arm64-ts.md └── server-ts.log依赖被刻意收窄脚本只使用标准库加httpxrequirements.txt 已有不引入 pytest从而在全新 checkout 上以单命令可运行。安全、清理与非目标安全与清理无论成败try/finally保证回收被测进程Windows 用taskkill /F /T杀整棵树与 Tauri 侧做法一致。关闭后验证端口已释放避免残留幽灵进程占住端口影响下一次运行。默认不动用户的 HF 缓存服务端继续按HF_HUB_CACHE/VOICEBOX_MODELS_DIR的既有语义使用缓存设计文档还提到可选的--isolated-cache冷启动模式把两个环境变量都指向 tempdir、每次全量重下但当前实现的 CLI 尚未包含该参数属于预留设计。非目标设计文档 Explicitly 排除不评估音质——没有 WER、没有波形比对通过仅指端点返回completed且产出非空 WAV不测 STTWhisper、效果链、频道或音频流式端点当前不在 CI 上运行为开发机人工触发CI 集成待脚本稳定后跟进各 run 之间不做模型卸载——模型保持已加载状态由服务端自行管理淘汰不校验二进制的版本漂移qwen_custom_voice 的 run 不覆盖instruct参数。这套设计的整体思路是把发布二进制是否真的能跑通全部 TTS 模型压缩成一次 10 行矩阵的串行回归用缓存感知的双超时吸收首次下载与纯推理的巨大耗时差异用 SSE 非空 WAV 校验定义最小可用的通过标准再用 JSON机器读与 Markdown人读双报告加失败行日志尾把诊断信息一次带全——使其既能作为发布前的人工验收工具也为后续 CI 化留下稳定接口。【免费下载链接】voiceboxThe open-source AI voice studio. Clone, dictate, create.项目地址: https://gitcode.com/GitHub_Trending/voicebox1/voicebox创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表