dlinkclaw-openclaw 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.en.md +46 -0
- package/README.md +15 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1 -0
- package/dist/package.json +95 -0
- package/dist/src/account.d.ts +54 -0
- package/dist/src/account.js +394 -0
- package/dist/src/channel.d.ts +17 -0
- package/dist/src/channel.js +153 -0
- package/dist/src/client/client-manager.d.ts +4 -0
- package/dist/src/client/client-manager.js +10 -0
- package/dist/src/client/const.d.ts +37 -0
- package/dist/src/client/const.js +155 -0
- package/dist/src/client/crypto.d.ts +47 -0
- package/dist/src/client/crypto.js +260 -0
- package/dist/src/client/dl-client.d.ts +117 -0
- package/dist/src/client/dl-client.js +588 -0
- package/dist/src/client/dl-http.d.ts +27 -0
- package/dist/src/client/dl-http.js +223 -0
- package/dist/src/client/media-handler.d.ts +21 -0
- package/dist/src/client/media-handler.js +196 -0
- package/dist/src/client/message.d.ts +96 -0
- package/dist/src/client/message.js +1211 -0
- package/dist/src/client/timeout.d.ts +20 -0
- package/dist/src/client/timeout.js +36 -0
- package/dist/src/client/types.d.ts +226 -0
- package/dist/src/client/types.js +1 -0
- package/dist/src/monitor.d.ts +18 -0
- package/dist/src/monitor.js +322 -0
- package/dist/src/outbound.d.ts +6 -0
- package/dist/src/outbound.js +78 -0
- package/dist/src/policy.d.ts +42 -0
- package/dist/src/policy.js +216 -0
- package/dist/src/runtime.d.ts +9 -0
- package/dist/src/runtime.js +65 -0
- package/dist/src/services/gateway-rpc.d.ts +37 -0
- package/dist/src/services/gateway-rpc.js +37 -0
- package/dist/src/services/index.d.ts +41 -0
- package/dist/src/services/index.js +245 -0
- package/dist/src/services/services.d.ts +169 -0
- package/dist/src/services/services.js +1160 -0
- package/dist/src/services/types.d.ts +391 -0
- package/dist/src/services/types.js +5 -0
- package/dist/src/services/ws-client.d.ts +186 -0
- package/dist/src/services/ws-client.js +896 -0
- package/dist/src/utils.d.ts +20 -0
- package/dist/src/utils.js +128 -0
- package/ext_capabilities/workrelay-speak/CLONE_GUIDE.md +129 -0
- package/ext_capabilities/workrelay-speak/HARDWARE.md +84 -0
- package/ext_capabilities/workrelay-speak/QWEN_GUIDE.md +151 -0
- package/ext_capabilities/workrelay-speak/README.md +79 -0
- package/ext_capabilities/workrelay-speak/__pycache__/patch-kokoro-en.cpython-312.pyc +0 -0
- package/ext_capabilities/workrelay-speak/install.sh +329 -0
- package/ext_capabilities/workrelay-speak/install.sh.bak-20260814 +376 -0
- package/ext_capabilities/workrelay-speak/manifest.json +63 -0
- package/ext_capabilities/workrelay-speak/patch-kokoro-en.py +85 -0
- package/ext_capabilities/workrelay-speak/status.sh +111 -0
- package/ext_capabilities/workrelay-speak/workrelay-tts.sh +118 -0
- package/ext_capabilities/workrelay-speak-luxtts/README.md +44 -0
- package/ext_capabilities/workrelay-speak-luxtts/install.sh +330 -0
- package/ext_capabilities/workrelay-speak-luxtts/manifest.json +63 -0
- package/ext_capabilities/workrelay-speak-luxtts/samples/45ea2e2c-0863-4966-a739-8bff8817f4c0.wav +0 -0
- package/ext_capabilities/workrelay-speak-luxtts/samples/800e5af4-cba5-441e-8003-6d36f35409e6.wav +0 -0
- package/ext_capabilities/workrelay-speak-luxtts/samples/85fafe01-d4d6-4a48-890e-9ae113f21905.wav +0 -0
- package/ext_capabilities/workrelay-speak-luxtts/samples/reference_text.txt +4 -0
- package/ext_capabilities/workrelay-speak-luxtts/samples/stone-sample-6.4s.wav +0 -0
- package/ext_capabilities/workrelay-speak-luxtts/status.sh +115 -0
- package/ext_capabilities/workrelay-speak-luxtts/workrelay-tts.sh +98 -0
- package/ext_capabilities/workrelay-speak-qwen/README.md +62 -0
- package/ext_capabilities/workrelay-speak-qwen/install.sh +349 -0
- package/ext_capabilities/workrelay-speak-qwen/manifest.json +63 -0
- package/ext_capabilities/workrelay-speak-qwen/status.sh +106 -0
- package/ext_capabilities/workrelay-speak-qwen/workrelay-tts.sh +98 -0
- package/ext_capabilities/workrelay-transcribe/README.md +76 -0
- package/ext_capabilities/workrelay-transcribe/__pycache__/transcribe.cpython-312.pyc +0 -0
- package/ext_capabilities/workrelay-transcribe/__pycache__/transcribe_server.cpython-312.pyc +0 -0
- package/ext_capabilities/workrelay-transcribe/install.sh +227 -0
- package/ext_capabilities/workrelay-transcribe/manifest.json +42 -0
- package/ext_capabilities/workrelay-transcribe/status.sh +63 -0
- package/ext_capabilities/workrelay-transcribe/transcribe.py +56 -0
- package/ext_capabilities/workrelay-transcribe/transcribe_server.py +116 -0
- package/ext_capabilities/workrelay-transcribe/voicebox-transcribe.sh +88 -0
- package/openclaw.plugin.json +142 -0
- package/package.json +95 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "workrelay-speak-qwen",
|
|
3
|
+
"displayName": "语音合成(千问 Qwen3-TTS)",
|
|
4
|
+
"description": "Agent 语音回复:Docker 部署 Voicebox(Qwen3-TTS CustomVoice 引擎),9 种预设音色,中英日韩多语种,音质自然、断句好,适合视频配音。独立容器(端口 17602);与 Kokoro / LuxTTS 互斥,启用本能力会自动停止其它 TTS。首次安装需下载模型约 4.6GB",
|
|
5
|
+
"requirements": [
|
|
6
|
+
{
|
|
7
|
+
"id": "os",
|
|
8
|
+
"label": "操作系统",
|
|
9
|
+
"expect": "Linux",
|
|
10
|
+
"check": "uname -s | grep -qi linux",
|
|
11
|
+
"hint": "TTS 依赖 Docker,仅支持 Linux(Windows 请用 WSL2)"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "docker",
|
|
15
|
+
"label": "Docker 已安装",
|
|
16
|
+
"expect": "已安装 docker",
|
|
17
|
+
"check": "command -v docker >/dev/null 2>&1",
|
|
18
|
+
"hint": "未找到 Docker,请先安装:sudo apt install docker.io docker-compose-v2"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "docker_access",
|
|
22
|
+
"label": "Docker 可访问(权限)",
|
|
23
|
+
"expect": "docker info 可正常执行",
|
|
24
|
+
"check": "docker info >/dev/null 2>&1 || (id -nG | grep -qw docker && sg docker -c 'docker info' >/dev/null 2>&1) || sudo -n docker info >/dev/null 2>&1",
|
|
25
|
+
"hint": "Docker 无权限。一次性解决(需 sudo 密码一次):① sudo systemctl start docker ② sudo usermod -aG docker $USER ③ openclaw gateway restart 后重试"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "memory",
|
|
29
|
+
"label": "内存 ≥ 6GB(建议 8GB+)",
|
|
30
|
+
"expect": "≥ 6GB 可用",
|
|
31
|
+
"check": "[ $(LANG=C free -m | awk '/^Mem:/{print $2}') -ge 6144 ]",
|
|
32
|
+
"hint": "当前内存不足,Qwen3-TTS 1.7B 至少需要 6GB 内存,建议 8GB 以上"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "disk",
|
|
36
|
+
"label": "磁盘 ≥ 8GB",
|
|
37
|
+
"expect": "≥ 8GB 可用(模型 4.6GB + 运行缓存)",
|
|
38
|
+
"check": "[ $(df -BM \"$HOME\" | tail -1 | awk '{print $4}' | sed 's/M//') -ge 8192 ]",
|
|
39
|
+
"hint": "可用空间不足,Qwen3-TTS 模型约需 4.6GB,建议预留 8GB"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "registry",
|
|
43
|
+
"label": "镜像仓库可达(ghcr.io / 国内公共仓库,自动回退)",
|
|
44
|
+
"expect": "能访问阿里云镜像仓库",
|
|
45
|
+
"check": "timeout 4 docker manifest inspect ghcr.io/rockychen2016/dlinkclaw-tts:qwen-20260914 >/dev/null 2>&1 || timeout 4 docker manifest inspect registry.cn-hangzhou.aliyuncs.com/shiduo/dlinkclaw-tts:qwen-20260914 >/dev/null 2>&1",
|
|
46
|
+
"hint": "镜像源不可达。国际源: ghcr.io(GitHub Container Registry,公开免登录);国内源: registry.cn-hangzhou.aliyuncs.com(待发布)。安装时自动按区域优先并回退"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "model_source",
|
|
50
|
+
"label": "模型源可达(HF 国内镜像)",
|
|
51
|
+
"expect": "能访问 hf-mirror.com",
|
|
52
|
+
"check": "timeout 4 curl -sI https://hf-mirror.com >/dev/null 2>&1 || timeout 4 curl -sI https://huggingface.co >/dev/null 2>&1",
|
|
53
|
+
"hint": "无法访问模型源(国内 hf-mirror.com / 国际 huggingface.co),Qwen 模型(4.6GB)将无法下载,请检查网络"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "gpu",
|
|
57
|
+
"label": "GPU 加速(可选)",
|
|
58
|
+
"expect": "检测到 NVIDIA GPU 时合成更快",
|
|
59
|
+
"check": "if command -v nvidia-smi >/dev/null 2>&1; then nvidia-smi -L >/dev/null 2>&1 && exit 0; fi; exit 0",
|
|
60
|
+
"hint": "未检测到 NVIDIA 独显,将使用 CPU 合成(Qwen 1.7B 约 10~60 秒/句,慢但可用);有 NVIDIA GPU 时自动启用 CUDA 加速"
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# WorkRelay 语音合成(千问 Qwen3-TTS)状态检查
|
|
3
|
+
# 返回值: 0=已启用, 1=未安装, 2=部分安装, 3=容器存在但API未就绪
|
|
4
|
+
# 2026-09-14 新增能力:独立容器 voicebox-qwen + 端口 17602 + provider tts-local-cli-qwen
|
|
5
|
+
# 2026-09-14 互斥:TTS 三选一——读 openclaw.json 的 tts.provider,不是本引擎则视为"未启用"
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
STATUS_OK=0
|
|
9
|
+
STATUS_NOT_INSTALLED=1
|
|
10
|
+
STATUS_PARTIAL=2
|
|
11
|
+
STATUS_DOCKER_DOWN=3
|
|
12
|
+
|
|
13
|
+
VB_HOST="${WR_VOICEBOX_HOST:-127.0.0.1}"
|
|
14
|
+
VB_PORT="${WR_VOICEBOX_QWEN_PORT:-17602}"
|
|
15
|
+
|
|
16
|
+
export PATH="$PATH:/snap/bin:/usr/local/bin"
|
|
17
|
+
if ! docker info &>/dev/null 2>&1; then
|
|
18
|
+
if sg docker -c "docker info" &>/dev/null 2>&1; then
|
|
19
|
+
docker() { sg docker -c "docker $(printf '%q ' "$@")"; }
|
|
20
|
+
elif sudo -n docker info &>/dev/null 2>&1; then
|
|
21
|
+
docker() { sudo docker "$@"; }
|
|
22
|
+
fi
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# 检查1: Docker
|
|
26
|
+
check_docker=0
|
|
27
|
+
docker info &>/dev/null 2>&1 && check_docker=1 || true
|
|
28
|
+
|
|
29
|
+
# 检查2: voicebox-qwen 容器
|
|
30
|
+
check_container=0
|
|
31
|
+
docker ps --format '{{.Names}}' 2>/dev/null | grep -q "voicebox-qwen" && check_container=1 || true
|
|
32
|
+
|
|
33
|
+
# 检查3: API
|
|
34
|
+
check_api=0
|
|
35
|
+
curl -s --max-time 3 "http://${VB_HOST}:${VB_PORT}/mcp" >/dev/null 2>&1 && check_api=1 || true
|
|
36
|
+
|
|
37
|
+
# 检查4: TTS 桥接脚本
|
|
38
|
+
check_tts_script=0
|
|
39
|
+
[ -f "$HOME/.workrelay/speak-qwen/workrelay-tts.sh" ] && check_tts_script=1 || true
|
|
40
|
+
|
|
41
|
+
# 检查5: 千问预设音色 profile
|
|
42
|
+
check_profile=0
|
|
43
|
+
[ -f "$HOME/.workrelay/speak-qwen/profile_id" ] && check_profile=1 || true
|
|
44
|
+
|
|
45
|
+
# 检查6: OpenClaw TTS 配置(2026-09-14 新版顶层路径 tts.providers.*,不再读旧 messages.tts.*)
|
|
46
|
+
# 2026-09-14 互斥判定:仅当 tts.provider == tts-local-cli-qwen 时才算"已启用"
|
|
47
|
+
check_config=0
|
|
48
|
+
check_provider=0
|
|
49
|
+
OC_JSON="${OPENCLAW_HOME:-$HOME/.openclaw}/openclaw.json"
|
|
50
|
+
if [ -f "$OC_JSON" ]; then
|
|
51
|
+
grep -q '"tts-local-cli-qwen"' "$OC_JSON" && check_config=1 || true
|
|
52
|
+
# 当前生效的 TTS provider(互斥判定):取 "provider": "xxx" 中第一个(tts 段内)
|
|
53
|
+
CUR_PROVIDER=$(python3 -c '
|
|
54
|
+
import json,sys
|
|
55
|
+
try:
|
|
56
|
+
c=json.load(open(sys.argv[1]))
|
|
57
|
+
print((c.get("tts") or {}).get("provider",""))
|
|
58
|
+
except Exception:
|
|
59
|
+
print("")
|
|
60
|
+
' "$OC_JSON" 2>/dev/null)
|
|
61
|
+
[ "$CUR_PROVIDER" = "tts-local-cli-qwen" ] && check_provider=1 || true
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# ── 汇总状态 ──
|
|
65
|
+
total=$((check_docker + check_container + check_api + check_tts_script + check_profile + check_config))
|
|
66
|
+
|
|
67
|
+
case $total in
|
|
68
|
+
6)
|
|
69
|
+
# 已安装齐全:再按互斥判定是否"启用"(provider 指向本引擎)
|
|
70
|
+
if [ "$check_provider" -eq 1 ]; then
|
|
71
|
+
status="enabled"; desc="语音合成(千问 Qwen3-TTS)已启用"; exit_code=$STATUS_OK
|
|
72
|
+
else
|
|
73
|
+
status="disabled"; desc="已安装但未启用(当前语音合成为其它引擎,TTS 只能启用一个)"; exit_code=$STATUS_NOT_INSTALLED
|
|
74
|
+
fi
|
|
75
|
+
;;
|
|
76
|
+
0) status="disabled"; desc="未安装"; exit_code=$STATUS_NOT_INSTALLED ;;
|
|
77
|
+
*)
|
|
78
|
+
if [ "$check_container" -eq 0 ] && [ "$check_docker" -eq 1 ]; then
|
|
79
|
+
status="disabled"; desc="Docker 运行中但 voicebox-qwen 未安装"; exit_code=$STATUS_NOT_INSTALLED
|
|
80
|
+
elif [ "$check_container" -eq 1 ] && [ "$check_api" -eq 0 ]; then
|
|
81
|
+
status="partial"; desc="voicebox-qwen 容器存在但 API 未就绪(可能在加载模型)"; exit_code=$STATUS_DOCKER_DOWN
|
|
82
|
+
else
|
|
83
|
+
status="partial"; desc="部分安装"; exit_code=$STATUS_PARTIAL
|
|
84
|
+
fi
|
|
85
|
+
;;
|
|
86
|
+
esac
|
|
87
|
+
|
|
88
|
+
# ── 输出 JSON ──
|
|
89
|
+
cat <<JSONEOF
|
|
90
|
+
{
|
|
91
|
+
"status": "$status",
|
|
92
|
+
"description": "$desc",
|
|
93
|
+
"endpoint": "http://${VB_HOST}:${VB_PORT}",
|
|
94
|
+
"checks": {
|
|
95
|
+
"docker": $( [ $check_docker -eq 1 ] && echo true || echo false ),
|
|
96
|
+
"container": $( [ $check_container -eq 1 ] && echo true || echo false ),
|
|
97
|
+
"api": $( [ $check_api -eq 1 ] && echo true || echo false ),
|
|
98
|
+
"tts_script": $( [ $check_tts_script -eq 1 ] && echo true || echo false ),
|
|
99
|
+
"profile": $( [ $check_profile -eq 1 ] && echo true || echo false ),
|
|
100
|
+
"openclaw_config": $( [ $check_config -eq 1 ] && echo true || echo false ),
|
|
101
|
+
"provider_active": $( [ $check_provider -eq 1 ] && echo true || echo false )
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
JSONEOF
|
|
105
|
+
|
|
106
|
+
exit $exit_code
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# WorkRelay TTS 桥接脚本(千问 Qwen3-TTS 引擎)
|
|
3
|
+
# 调用本地 Voicebox (voicebox-qwen, 端口 17602) REST API
|
|
4
|
+
# 环境变量:
|
|
5
|
+
# WR_VOICEBOX_QWEN_PORT - 千问实例端口(默认 17602,独立于 Kokoro 17493/17600、LuxTTS 17601)
|
|
6
|
+
# WR_VOICEBOX_PROFILE_ID - 声音 profile ID(可选,缺省读取 ~/.workrelay/speak-qwen/profile_id)
|
|
7
|
+
# WR_VOICEBOX_LANG - 语言(默认 zh)
|
|
8
|
+
# 2026-09-14 新增:千问 Qwen3-TTS 能力,固定引擎 qwen_custom_voice(预设音色)
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
TEXT="$1"
|
|
12
|
+
OUT="$2"
|
|
13
|
+
|
|
14
|
+
if [ -z "$TEXT" ] || [ -z "$OUT" ]; then
|
|
15
|
+
echo "usage: $0 <text> <output_path>" >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
VB_HOST="${WR_VOICEBOX_HOST:-127.0.0.1}"
|
|
20
|
+
VB_PORT="${WR_VOICEBOX_QWEN_PORT:-17602}"
|
|
21
|
+
PROFILE_ID="${WR_VOICEBOX_PROFILE_ID:-}"
|
|
22
|
+
# 固定引擎:千问预设音色引擎
|
|
23
|
+
ENGINE="qwen_custom_voice"
|
|
24
|
+
LANG="${WR_VOICEBOX_LANG:-zh}"
|
|
25
|
+
|
|
26
|
+
BASE_URL="http://${VB_HOST}:${VB_PORT}"
|
|
27
|
+
if ! curl -s --max-time 2 "${BASE_URL}/mcp" >/dev/null 2>&1; then
|
|
28
|
+
echo "ERROR: Voicebox (千问) API 不可达 ($VB_HOST:$VB_PORT)" >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# ── profile 自动解析(install.sh 写入千问预设音色 profile id)──
|
|
33
|
+
if [ -z "$PROFILE_ID" ] && [ -f "$HOME/.workrelay/speak-qwen/profile_id" ]; then
|
|
34
|
+
PROFILE_ID=$(cat "$HOME/.workrelay/speak-qwen/profile_id" 2>/dev/null | tr -d ' \n')
|
|
35
|
+
fi
|
|
36
|
+
if [ -z "$PROFILE_ID" ]; then
|
|
37
|
+
# 兜底:从 /profiles 找千问预设 profile(优先中文女声 Vivian)
|
|
38
|
+
PROFILE_ID=$(curl --noproxy '*' -s --max-time 5 "$BASE_URL/profiles" 2>/dev/null \
|
|
39
|
+
| python3 -c '
|
|
40
|
+
import json,sys
|
|
41
|
+
out=""
|
|
42
|
+
try:
|
|
43
|
+
d=json.load(sys.stdin)
|
|
44
|
+
for p in d:
|
|
45
|
+
if p.get("preset_engine")=="qwen_custom_voice" and p.get("language")=="zh":
|
|
46
|
+
out=p.get("id",""); break
|
|
47
|
+
if not out:
|
|
48
|
+
for p in d:
|
|
49
|
+
if p.get("preset_engine")=="qwen_custom_voice":
|
|
50
|
+
out=p.get("id",""); break
|
|
51
|
+
print(out)
|
|
52
|
+
except Exception:
|
|
53
|
+
print("")
|
|
54
|
+
' 2>/dev/null)
|
|
55
|
+
fi
|
|
56
|
+
if [ -z "$PROFILE_ID" ]; then
|
|
57
|
+
echo "ERROR: 未找到可用的千问预设音色 profile(Voicebox /profiles 为空)" >&2
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# JSON-escape text
|
|
62
|
+
TEXT_JSON=$(python3 -c 'import json,sys; print(json.dumps(sys.argv[1]))' "$TEXT")
|
|
63
|
+
|
|
64
|
+
# 提交生成请求(固定 engine=qwen_custom_voice)
|
|
65
|
+
RESP=$(curl --noproxy '*' -s --max-time 15 -X POST "$BASE_URL/generate" \
|
|
66
|
+
-H "Content-Type: application/json" \
|
|
67
|
+
-H "X-Voicebox-Client-Id: workrelay-tts-qwen" \
|
|
68
|
+
-d "{\"text\": $TEXT_JSON, \"profile_id\": \"$PROFILE_ID\", \"language\": \"$LANG\", \"engine\": \"$ENGINE\"}")
|
|
69
|
+
|
|
70
|
+
GEN_ID=$(echo "$RESP" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("id",""))' 2>/dev/null)
|
|
71
|
+
if [ -z "$GEN_ID" ]; then
|
|
72
|
+
echo "ERROR: Voicebox (千问) 生成提交失败: $RESP" >&2
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# 轮询等待完成(千问 1.7B CPU 推理约 10~180 秒,GPU 更快)
|
|
77
|
+
MAX_POLLS=300
|
|
78
|
+
for i in $(seq 1 $MAX_POLLS); do
|
|
79
|
+
sleep 1
|
|
80
|
+
ST=$(curl --noproxy '*' -s --max-time 10 "$BASE_URL/history/$GEN_ID" 2>/dev/null \
|
|
81
|
+
| python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("status",""))' 2>/dev/null)
|
|
82
|
+
if [ "$ST" = "completed" ]; then
|
|
83
|
+
break
|
|
84
|
+
fi
|
|
85
|
+
if [ "$ST" = "error" ] || [ "$ST" = "failed" ]; then
|
|
86
|
+
echo "ERROR: Voicebox (千问) 生成失败 (id=$GEN_ID, status=$ST)" >&2
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
done
|
|
90
|
+
|
|
91
|
+
# 下载音频
|
|
92
|
+
curl --noproxy '*' -s --max-time 60 -o "$OUT" "$BASE_URL/audio/$GEN_ID"
|
|
93
|
+
if [ -s "$OUT" ]; then
|
|
94
|
+
exit 0
|
|
95
|
+
else
|
|
96
|
+
echo "ERROR: Voicebox (千问) 音频下载失败 (id=$GEN_ID)" >&2
|
|
97
|
+
exit 1
|
|
98
|
+
fi
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# WorkRelay 语音转录模块
|
|
2
|
+
|
|
3
|
+
## 文件说明
|
|
4
|
+
|
|
5
|
+
| 文件 | 作用 |
|
|
6
|
+
|------|------|
|
|
7
|
+
| `transcribe.py` | faster-whisper 转录引擎(纯 Python,CPU 推理) |
|
|
8
|
+
| `transcribe_server.py` | **常驻转写服务**(2026-08-12 新增):模型加载一次常驻内存,免每次重新加载 |
|
|
9
|
+
| `voicebox-transcribe.sh` | 转录桥接:优先常驻服务 → 直接本地 → 可选远程 fallback |
|
|
10
|
+
| `install.sh` | 一键安装(依赖→模型→启动常驻服务→OpenClaw 配置) |
|
|
11
|
+
| `status.sh` | 状态查询,输出 JSON,客户端判断"已启用/未启用" |
|
|
12
|
+
|
|
13
|
+
## 客户端集成方案
|
|
14
|
+
|
|
15
|
+
### 设置页面
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────────────────────────┐
|
|
19
|
+
│ ⚙️ 语音转录 │
|
|
20
|
+
│ │
|
|
21
|
+
│ 状态: ● 已启用 / ○ 未启用 │
|
|
22
|
+
│ │
|
|
23
|
+
│ 模型: faster-whisper-small │
|
|
24
|
+
│ 存储: ~460MB(常驻内存) │
|
|
25
|
+
│ │
|
|
26
|
+
│ [ 启用语音转录 ] ← 仅未启用时显示 │
|
|
27
|
+
└─────────────────────────────────────┘
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 交互流程
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
客户端设置页 → 点击[启用] → 发送指令给 Agent
|
|
34
|
+
→ Agent 执行 install.sh
|
|
35
|
+
→ 返回进度/结果
|
|
36
|
+
→ 客户端刷新状态
|
|
37
|
+
|
|
38
|
+
客户端设置页 → 加载时 → 调用 status.sh
|
|
39
|
+
→ 解析 JSON
|
|
40
|
+
→ 显示状态
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### status.sh 返回格式
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"status": "enabled", // enabled | disabled | partial
|
|
48
|
+
"description": "语音转录已启用",
|
|
49
|
+
"checks": {
|
|
50
|
+
"faster_whisper": true,
|
|
51
|
+
"model": true,
|
|
52
|
+
"scripts": true,
|
|
53
|
+
"openclaw_config": true
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
status 为 `enabled` 时四项全部为 true。
|
|
59
|
+
|
|
60
|
+
## 环境变量(可选)
|
|
61
|
+
|
|
62
|
+
| 变量 | 默认值 | 说明 |
|
|
63
|
+
|------|--------|------|
|
|
64
|
+
| `WR_WHISPER_MODEL` | `small` | 模型大小(small 默认,广东话/普通话均可用) |
|
|
65
|
+
| `WR_WHISPER_CACHE` | `~/.cache/faster-whisper` | 模型缓存目录 |
|
|
66
|
+
| `WR_WHISPER_PORT` | `17890` | 常驻服务端口 |
|
|
67
|
+
| `WR_INSTALL_DIR` | `~/.workrelay/transcribe` | 脚本安装目录 |
|
|
68
|
+
| `WR_FALLBACK_URL` | 无 | 远程转录服务 URL(不设则仅本地) |
|
|
69
|
+
|
|
70
|
+
## 限制
|
|
71
|
+
|
|
72
|
+
- 仅支持 **Linux / macOS**(Windows 用户需 WSL2)
|
|
73
|
+
- 需要 **1GB+** 可用磁盘空间
|
|
74
|
+
- 需要可访问 HuggingFace(下载模型,small ~460MB)
|
|
75
|
+
- 常驻服务首次启动加载模型约 5-10 秒,之后转写即时(3-5 秒)
|
|
76
|
+
- OpenClaw 内置 `MIN_AUDIO_FILE_BYTES=1024`,小于 1KB 的语音不会触发转录
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# WorkRelay 语音转录一键安装脚本
|
|
3
|
+
# 自动完成:依赖安装 → 模型下载 → OpenClaw 配置
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
RED='\033[0;31m'
|
|
7
|
+
GREEN='\033[0;32m'
|
|
8
|
+
YELLOW='\033[1;33m'
|
|
9
|
+
NC='\033[0m'
|
|
10
|
+
|
|
11
|
+
INSTALL_DIR="$HOME/.workrelay/transcribe"
|
|
12
|
+
MODEL_CACHE="$HOME/.cache/faster-whisper"
|
|
13
|
+
WR_DIR="$(dirname "$(readlink -f "$0")")"
|
|
14
|
+
|
|
15
|
+
echo "============================================"
|
|
16
|
+
echo " WorkRelay 语音转录 - 一键安装"
|
|
17
|
+
echo "============================================"
|
|
18
|
+
echo ""
|
|
19
|
+
|
|
20
|
+
# ── 系统检查 ──
|
|
21
|
+
echo -n "[1/5] 检查系统环境... "
|
|
22
|
+
|
|
23
|
+
OS="$(uname -s)"
|
|
24
|
+
case "$OS" in
|
|
25
|
+
Linux) ;;
|
|
26
|
+
Darwin) ;;
|
|
27
|
+
*)
|
|
28
|
+
echo -e "${RED}失败${NC}"
|
|
29
|
+
echo "错误:当前仅支持 Linux/macOS,不支持 $OS"
|
|
30
|
+
echo "提示:Windows 用户请通过 WSL2 运行本脚本"
|
|
31
|
+
exit 1
|
|
32
|
+
;;
|
|
33
|
+
esac
|
|
34
|
+
|
|
35
|
+
if ! command -v python3 &>/dev/null; then
|
|
36
|
+
echo -e "${RED}失败${NC}"
|
|
37
|
+
echo "错误:未找到 python3,请先安装 Python 3.8+"
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
PYVER=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
|
|
42
|
+
echo -e "${GREEN}OK${NC} (Python $PYVER, $(uname -o))"
|
|
43
|
+
|
|
44
|
+
# ── 磁盘空间检查 ──
|
|
45
|
+
echo -n "[2/5] 检查磁盘空间... "
|
|
46
|
+
AVAIL=$(df -BM "$HOME" | tail -1 | awk '{print $4}' | sed 's/M//')
|
|
47
|
+
if [ "$AVAIL" -lt 2500 ]; then
|
|
48
|
+
echo -e "${RED}不足${NC}"
|
|
49
|
+
echo "错误:可用空间 ${AVAIL}MB,至少需要 1GB(模型 ~460MB)"
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
echo -e "${GREEN}OK${NC} (可用 ${AVAIL}MB)"
|
|
53
|
+
|
|
54
|
+
# ── 安装 Python 依赖 ──
|
|
55
|
+
echo -n "[3/5] 安装 Python 依赖... "
|
|
56
|
+
|
|
57
|
+
# 2026-08-18 修复:彻底移除 sudo 依赖,改用户级安装(pip --user / ensurepip --user)。
|
|
58
|
+
# 纯 Linux 上 sudo 通常需要密码,spawn 非交互环境无法输入 → 原 sudo apt-get 方案必失败。
|
|
59
|
+
# 用户级安装无需任何提权,任何机器可用;--break-system-packages 兼容 PEP668 系统。
|
|
60
|
+
export PATH="$PATH:$HOME/.local/bin:/usr/local/bin:/snap/bin"
|
|
61
|
+
|
|
62
|
+
# 统一 pip 调用入口(pip3 优先,否则 python3 -m pip)
|
|
63
|
+
pip_install() {
|
|
64
|
+
if command -v pip3 >/dev/null 2>&1; then
|
|
65
|
+
pip3 "$@"
|
|
66
|
+
else
|
|
67
|
+
python3 -m pip "$@"
|
|
68
|
+
fi
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# 确保 pip 可用:python3 -m pip / pip3 都没有时,用 ensurepip --user 自举(Python 自带,无需 sudo)
|
|
72
|
+
if ! python3 -m pip --version &>/dev/null 2>&1 && ! command -v pip3 &>/dev/null; then
|
|
73
|
+
echo ""
|
|
74
|
+
echo " 未检测到 pip,使用 python3 -m ensurepip --user 自举安装 ..."
|
|
75
|
+
if ! python3 -m ensurepip --user >/tmp/wr-pip-err.log 2>&1; then
|
|
76
|
+
echo -e "${RED}失败${NC}"
|
|
77
|
+
echo "错误:pip 自举失败,请手动执行:"
|
|
78
|
+
echo " python3 -m ensurepip --user"
|
|
79
|
+
echo " 或安装系统 pip(需 root):sudo apt install python3-pip"
|
|
80
|
+
cat /tmp/wr-pip-err.log
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
export PATH="$PATH:$HOME/.local/bin"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# 安装 faster-whisper(用户级 --user,不写系统目录,无需 sudo)
|
|
87
|
+
echo " pip 可用,安装 faster-whisper(用户级安装)..."
|
|
88
|
+
if ! pip_install install --user --quiet --break-system-packages faster-whisper 2>/tmp/wr-pip-err.log; then
|
|
89
|
+
# fallback:老系统无 PEP668,无需 --break-system-packages
|
|
90
|
+
if ! pip_install install --user --quiet faster-whisper 2>>/tmp/wr-pip-err.log; then
|
|
91
|
+
echo -e "${RED}失败${NC}"
|
|
92
|
+
echo "错误:faster-whisper 安装失败,请手动执行:"
|
|
93
|
+
echo " pip3 install --user --break-system-packages faster-whisper"
|
|
94
|
+
cat /tmp/wr-pip-err.log
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
echo -e "${GREEN}OK${NC}"
|
|
99
|
+
|
|
100
|
+
# ── 下载模型 ──
|
|
101
|
+
echo -n "[4/5] 下载 Whisper 模型 (~460MB)... "
|
|
102
|
+
echo ""
|
|
103
|
+
# 2026-08-18 修复:systemd 透传的 all_proxy 可能是 socks5://(如 clash),
|
|
104
|
+
# 而 httpx 初始化 SOCKS 代理需要 socksio 包(默认未装)→ ImportError 导致下载失败。
|
|
105
|
+
# clash 混合端口(7890)同时支持 HTTP,这里将 socks5:// 降级为 http://,无需额外装包。
|
|
106
|
+
if [[ "${all_proxy:-}${ALL_PROXY:-}" == *"socks5://"* ]]; then
|
|
107
|
+
echo " ℹ 检测到 socks5 代理,降级为 http 代理(clash 混合端口兼容),避免 socksio 依赖"
|
|
108
|
+
export all_proxy="${all_proxy//socks5:/http:}"
|
|
109
|
+
export ALL_PROXY="${ALL_PROXY//socks5:/http:}"
|
|
110
|
+
fi
|
|
111
|
+
# 2026-08-18 修复:国内机器直连 HuggingFace 经常 Network unreachable,
|
|
112
|
+
# 增加 hf-mirror.com 镜像自动 fallback(HF_ENDPOINT 环境变量由 huggingface_hub 原生支持)。
|
|
113
|
+
# 直连失败 → 镜像重试 → 仍失败给出手动指引。
|
|
114
|
+
dl_model() {
|
|
115
|
+
python3 -c "
|
|
116
|
+
import os
|
|
117
|
+
from huggingface_hub import snapshot_download
|
|
118
|
+
endpoint = os.environ.get('HF_ENDPOINT', 'https://huggingface.co')
|
|
119
|
+
print(f' 正在从 {endpoint} 下载 faster-whisper-small ...')
|
|
120
|
+
snapshot_download('Systran/faster-whisper-small',
|
|
121
|
+
cache_dir='$MODEL_CACHE')
|
|
122
|
+
print(' 模型下载完成')
|
|
123
|
+
" 2>&1
|
|
124
|
+
}
|
|
125
|
+
# 2026-09-14 优化(石头哥):按区域决定下载顺序
|
|
126
|
+
# 国内(WR_IMAGE_REGION=cn,客户端中文界面传入)→ 镜像优先,省掉直连超时等待
|
|
127
|
+
# 国际/未指定 → 直连优先;两种顺序都保留"失败自动回退另一种"+ 手动指引
|
|
128
|
+
HF_MIRROR_URL="${WR_HF_ENDPOINT:-https://hf-mirror.com}"
|
|
129
|
+
dl_fail_msg() {
|
|
130
|
+
echo -e "${RED}模型下载失败${NC}"
|
|
131
|
+
echo "提示:检查网络连接,或手动设置 WR_WHISPER_CACHE 到已有模型目录"
|
|
132
|
+
echo " 也可手动下载:HF_ENDPOINT=${HF_MIRROR_URL} python3 -c \"from huggingface_hub import snapshot_download; snapshot_download('Systran/faster-whisper-small', cache_dir='$MODEL_CACHE')\""
|
|
133
|
+
}
|
|
134
|
+
if [ "${WR_IMAGE_REGION:-auto}" = "cn" ] && [ -z "${WR_HF_ENDPOINT:-}" ]; then
|
|
135
|
+
# 国内:镜像优先(避免先直连超时白等)
|
|
136
|
+
if HF_ENDPOINT="$HF_MIRROR_URL" dl_model; then
|
|
137
|
+
echo " ✅ 已通过国内镜像 hf-mirror.com 下载(国内优先,跳过直连等待)"
|
|
138
|
+
elif dl_model; then
|
|
139
|
+
echo " ℹ 国内镜像不可用,已直连 HuggingFace 下载成功"
|
|
140
|
+
else
|
|
141
|
+
dl_fail_msg
|
|
142
|
+
exit 1
|
|
143
|
+
fi
|
|
144
|
+
else
|
|
145
|
+
# 国际/自动:直连优先,失败回退镜像
|
|
146
|
+
if dl_model; then
|
|
147
|
+
:
|
|
148
|
+
elif HF_ENDPOINT="$HF_MIRROR_URL" dl_model; then
|
|
149
|
+
echo " ℹ 直连 HuggingFace 失败,已通过国内镜像 hf-mirror.com 下载成功"
|
|
150
|
+
else
|
|
151
|
+
dl_fail_msg
|
|
152
|
+
exit 1
|
|
153
|
+
fi
|
|
154
|
+
fi
|
|
155
|
+
echo -e " ${GREEN}OK${NC}"
|
|
156
|
+
|
|
157
|
+
# ── 安装脚本 & 配置 OpenClaw ──
|
|
158
|
+
echo -n "[5/5] 部署脚本 & 配置 OpenClaw... "
|
|
159
|
+
mkdir -p "$INSTALL_DIR"
|
|
160
|
+
cp "$WR_DIR/transcribe.py" "$INSTALL_DIR/"
|
|
161
|
+
cp "$WR_DIR/transcribe_server.py" "$INSTALL_DIR/"
|
|
162
|
+
cp "$WR_DIR/voicebox-transcribe.sh" "$INSTALL_DIR/"
|
|
163
|
+
chmod +x "$INSTALL_DIR/voicebox-transcribe.sh" "$INSTALL_DIR/transcribe.py" "$INSTALL_DIR/transcribe_server.py"
|
|
164
|
+
|
|
165
|
+
# 2026-08-12:启动常驻转写服务(模型常驻内存,免每次重新加载)。
|
|
166
|
+
# 已在跑则跳过;未跑则后台 detached 启动,日志写入 ~/.openclaw/logs/wr-transcribe-server.log。
|
|
167
|
+
WR_SERVER_PORT="${WR_WHISPER_PORT:-17890}"
|
|
168
|
+
if curl -s --connect-timeout 2 "http://127.0.0.1:${WR_SERVER_PORT}/health" >/dev/null 2>&1; then
|
|
169
|
+
echo -n " (常驻服务已运行) "
|
|
170
|
+
else
|
|
171
|
+
mkdir -p "$(dirname "$HOME/.openclaw/logs/wr-transcribe-server.log" 2>/dev/null)" 2>/dev/null || true
|
|
172
|
+
nohup python3 "$INSTALL_DIR/transcribe_server.py" "$WR_SERVER_PORT" \
|
|
173
|
+
>>"$HOME/.openclaw/logs/wr-transcribe-server.log" 2>&1 &
|
|
174
|
+
echo -n " (常驻服务已启动) "
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# 写入 OpenClaw 配置
|
|
178
|
+
if command -v openclaw &>/dev/null; then
|
|
179
|
+
openclaw config set audio.transcription.command \
|
|
180
|
+
"[\"$INSTALL_DIR/voicebox-transcribe.sh\", \"{{MediaPath}}\"]" 2>/dev/null || {
|
|
181
|
+
echo -e "${YELLOW}⚠ OpenClaw 配置写入失败,请手动执行:${NC}"
|
|
182
|
+
echo " openclaw config set audio.transcription.command '[\"$INSTALL_DIR/voicebox-transcribe.sh\", \"{{MediaPath}}\"]'"
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
# 2026-08-12:入站语音转写切到本地 faster-whisper(tools.media.audio → cli 模式)。
|
|
186
|
+
# 默认 OpenClaw 入站音频转写走 tools.media.audio 的云端 provider(如商汤 SenseAudio),
|
|
187
|
+
# 安装本扩展后应切到本地命令,避免联网/费用;用户无需手动改配置。
|
|
188
|
+
openclaw config set tools.media.audio.models \
|
|
189
|
+
'[{"type":"cli","command":"'"$INSTALL_DIR"'/voicebox-transcribe.sh","args":["{{MediaPath}}"]}]' 2>/dev/null || {
|
|
190
|
+
echo -e "${YELLOW}⚠ tools.media.audio 切本地失败,请手动配置${NC}"
|
|
191
|
+
}
|
|
192
|
+
openclaw gateway restart 2>/dev/null || true
|
|
193
|
+
else
|
|
194
|
+
echo -e "${YELLOW}⚠ 未检测到 openclaw CLI,跳过自动配置${NC}"
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
echo -e "${GREEN}OK${NC}"
|
|
198
|
+
echo ""
|
|
199
|
+
|
|
200
|
+
# ── 验证 ──
|
|
201
|
+
echo "============================================"
|
|
202
|
+
echo " 安装验证"
|
|
203
|
+
echo "============================================"
|
|
204
|
+
|
|
205
|
+
echo -n " faster-whisper: "
|
|
206
|
+
python3 -c "import faster_whisper; print(faster_whisper.__version__)" 2>/dev/null \
|
|
207
|
+
&& echo "" \
|
|
208
|
+
|| echo -e "${RED}未安装${NC}"
|
|
209
|
+
|
|
210
|
+
echo -n " 模型: "
|
|
211
|
+
if [ -d "$MODEL_CACHE" ] && [ "$(du -sm "$MODEL_CACHE" | cut -f1)" -gt 500 ]; then
|
|
212
|
+
echo -e "${GREEN}$(du -sh "$MODEL_CACHE" | cut -f1)${NC}"
|
|
213
|
+
else
|
|
214
|
+
echo -e "${RED}未下载或异常${NC}"
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
echo -n " 转录脚本: "
|
|
218
|
+
if [ -f "$INSTALL_DIR/transcribe.py" ] && [ -f "$INSTALL_DIR/voicebox-transcribe.sh" ]; then
|
|
219
|
+
echo -e "${GREEN}已部署${NC} ($INSTALL_DIR)"
|
|
220
|
+
else
|
|
221
|
+
echo -e "${RED}缺失${NC}"
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
echo ""
|
|
225
|
+
echo -e "${GREEN}✅ 安装完成!${NC}"
|
|
226
|
+
echo " 语音转录将在下次 OpenClaw 重启后生效。"
|
|
227
|
+
echo ""
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "workrelay-transcribe",
|
|
3
|
+
"displayName": "语音转录(STT)",
|
|
4
|
+
"description": "语音消息转文字:本地 faster-whisper 转录,无需联网",
|
|
5
|
+
"requirements": [
|
|
6
|
+
{
|
|
7
|
+
"id": "os",
|
|
8
|
+
"label": "操作系统",
|
|
9
|
+
"expect": "Linux / macOS",
|
|
10
|
+
"check": "uname -s | grep -qiE 'linux|darwin'",
|
|
11
|
+
"hint": "仅支持 Linux/macOS(Windows 请用 WSL2)"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "python",
|
|
15
|
+
"label": "Python 3.8+",
|
|
16
|
+
"expect": "已安装 python3",
|
|
17
|
+
"check": "command -v python3 >/dev/null 2>&1 && python3 -c 'import sys; sys.exit(0 if sys.version_info >= (3, 8) else 1)'",
|
|
18
|
+
"hint": "未找到 Python 3.8+,请先安装 Python"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "pip",
|
|
22
|
+
"label": "pip 可用",
|
|
23
|
+
"expect": "pip3 或 python3 -m pip",
|
|
24
|
+
"check": "command -v pip3 >/dev/null 2>&1 || python3 -m pip --version >/dev/null 2>&1",
|
|
25
|
+
"hint": "未找到 pip 时,安装脚本会自动用 python3 -m ensurepip --user 用户级自举,无需手动安装"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "disk",
|
|
29
|
+
"label": "磁盘 ≥ 2.5GB",
|
|
30
|
+
"expect": "≥ 2.5GB 可用",
|
|
31
|
+
"check": "[ $(df -BM \"$HOME\" | tail -1 | awk '{print $4}' | sed 's/M//') -ge 2560 ]",
|
|
32
|
+
"hint": "可用空间不足,模型约需 460MB,建议预留 1GB"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "network",
|
|
36
|
+
"label": "外网可达(HuggingFace 或镜像)",
|
|
37
|
+
"expect": "能访问 huggingface.co 或 hf-mirror.com",
|
|
38
|
+
"check": "curl -sI --max-time 5 https://huggingface.co >/dev/null 2>&1 || curl -sI --max-time 5 https://hf-mirror.com >/dev/null 2>&1",
|
|
39
|
+
"hint": "无法访问 HuggingFace 及国内镜像,将无法下载 Whisper 模型"
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|