feat(asr): add pluggable ONNX streaming backend and model registry
Add a second backend (sherpa-onnx) alongside llama.cpp, so native streaming models can be selected by ID from a JSON registry. All such models share one adapter: only the model files and a few options differ. Models now live under the app data directory and are fetched by scripts/fetch-models.sh. Adding a model is a data change, not code. Measured on the same audio: the streaming backend is ~35x cheaper in CPU than Qwen3-ASR (0.10-0.12 vs 4.27 core-seconds per audio second), shows the first caption at 0.48-1.44s, and matches its accuracy for zh/en. 新增 sherpa-onnx 流式后端,与 llama.cpp 并存;模型按注册表 id 从应用 数据目录加载。同族模型共用一个适配器,新增模型只改 data/models.json。 实测流式后端 CPU 约为 Qwen3-ASR 的 1/35,首个字幕 0.48~1.44s,中英 准确率持平。注意预编译库是旧 C++ ABI,必须走其 C API。
This commit is contained in:
1 parent
6d64987d64
commit
bd921b1ac5
15 files changed
+869
-18
No files matched your search
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env bash
|
||||
# 把模型注册表里的模型下载到应用数据目录(QStandardPaths::AppDataLocation)。
|
||||
#
|
||||
# 用法:
|
||||
# ./fetch-models.sh # 列出所有模型和安装状态
|
||||
# ./fetch-models.sh <id> [...] # 下载指定模型
|
||||
# ./fetch-models.sh --all # 下载全部
|
||||
#
|
||||
# 目录规则(与程序里 ModelStore 保持一致):
|
||||
# ${XDG_DATA_HOME:-~/.local/share}/live-caption/models/<id>/
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
REG="$ROOT/data/models.json"
|
||||
DEST="${XDG_DATA_HOME:-$HOME/.local/share}/live-caption/models"
|
||||
|
||||
[ -f "$REG" ] || { echo "找不到注册表: $REG" >&2; exit 1; }
|
||||
|
||||
ids=$(python3 -c "
|
||||
import json,sys
|
||||
d=json.load(open('$REG'))
|
||||
print(' '.join(m['id'] for m in d['models']))")
|
||||
|
||||
show_status() {
|
||||
python3 - "$REG" "$DEST" <<'PY'
|
||||
import json, os, sys
|
||||
reg, dest = sys.argv[1], sys.argv[2]
|
||||
d = json.load(open(reg))
|
||||
for m in d['models']:
|
||||
mid = m['id']; mdir = os.path.join(dest, mid)
|
||||
files = m['files']
|
||||
have = sum(1 for f in files.values() if os.path.exists(os.path.join(mdir, f['name'])))
|
||||
total = len(files)
|
||||
mark = "已安装" if have == total else ("部分" if have else "未安装")
|
||||
langs = ",".join(m['languages'][:4]) + ("…" if len(m['languages']) > 4 else "")
|
||||
print(f" [{have}/{total} {mark}] {mid:22s} {langs:18s} {m.get('license','')}")
|
||||
print(f" {m['name']}")
|
||||
PY
|
||||
}
|
||||
|
||||
download_one() {
|
||||
local id="$1"
|
||||
local dir="$DEST/$id"
|
||||
mkdir -p "$dir"
|
||||
echo "==> $id → $dir"
|
||||
python3 - "$REG" "$id" <<'PY' | while IFS=$'\t' read -r name url; do
|
||||
import json, sys
|
||||
d = json.load(open(sys.argv[1])); mid = sys.argv[2]
|
||||
m = next(x for x in d['models'] if x['id'] == mid)
|
||||
for f in m['files'].values():
|
||||
print(f"{f['name']}\t{f['url']}")
|
||||
PY
|
||||
if [ -f "$dir/$name" ]; then
|
||||
echo " 已存在,跳过: $name"
|
||||
continue
|
||||
fi
|
||||
echo " 下载 $name"
|
||||
if command -v aria2c >/dev/null 2>&1; then
|
||||
aria2c -x16 -s16 -k4M --summary-interval=0 -d "$dir" -o "$name" "$url" >/dev/null
|
||||
else
|
||||
curl -fL --progress-bar -o "$dir/$name" "$url"
|
||||
fi
|
||||
done
|
||||
echo " 完成"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
"" ) echo "模型存储目录: $DEST"; echo; show_status ;;
|
||||
--all) shift; for id in $ids; do download_one "$id"; done ;;
|
||||
*) for id in "$@"; do
|
||||
echo "$ids" | tr ' ' '\n' | grep -qx "$id" || { echo "未知模型: $id" >&2; exit 1; }
|
||||
download_one "$id"
|
||||
done ;;
|
||||
esac
|
||||
Reference in new issue
Block a user