Add official FunASR runtime websocket service

This commit is contained in:
Xulilong 2026-07-16 09:41:24 +08:00
parent f3d207a6fb
commit 4ec8bfb93b
4 changed files with 81 additions and 25 deletions

View File

@ -43,7 +43,8 @@ docker compose down
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Speakr | `voice-speakr` | `8899` | 页面和后端 | | Speakr | `voice-speakr` | `8899` | 页面和后端 |
| ASR | `asr-test` | `59805` | Paraformer HTTP / WebSocket 识别 | | ASR | `asr-test` | `59805` | Paraformer HTTP / WebSocket 识别 |
| 官方实时 ASR | `funasr-online` | `10096` | FunASR 官方实时 WebSocket | | Python 实时 ASR | `funasr-online` | `10096` | FunASR Python WebSocket保留用于回退 |
| 官方 Runtime 实时 ASR | `funasr-runtime-2pass` | `10097` | FunASR 官方 C++/ONNX 2pass WebSocket |
| 声纹提取 | `voice-dected` | `18000` | `/extract_embedding` 声纹向量服务 | | 声纹提取 | `voice-dected` | `18000` | `/extract_embedding` 声纹向量服务 |
| 实时桥接 | `fastapi-wss` | `10095` | 页面实时 ASR WebSocket 桥接 | | 实时桥接 | `fastapi-wss` | `10095` | 页面实时 ASR WebSocket 桥接 |
| Redis | `voice-redis` | `16380` | 实时桥接的缓存/会话存储 | | Redis | `voice-redis` | `16380` | 实时桥接的缓存/会话存储 |
@ -86,7 +87,9 @@ docker compose up -d
```env ```env
ASR_BASE_URL=http://asr-test:59805 ASR_BASE_URL=http://asr-test:59805
FUNASR_WEBSOCKET_IP=ws://fastapi-wss:10095/websocket_offline FUNASR_WEBSOCKET_IP=ws://fastapi-wss:10095/websocket_offline
TARGET_WS_URL=ws://funasr-online:10096 TARGET_WS_URL=ws://funasr-runtime-2pass:10095
# 如需回退到 Python 版实时 ASR可改为
# TARGET_WS_URL=ws://funasr-online:10096
VOICE_DETECTED_URL=http://voice-dected:8000 VOICE_DETECTED_URL=http://voice-dected:8000
SPEAKER_DET_URL=http://voice-dected:8000/extract_embedding SPEAKER_DET_URL=http://voice-dected:8000/extract_embedding
REDIS_URL=redis://redis:6379/0 REDIS_URL=redis://redis:6379/0

View File

@ -14,8 +14,10 @@ TEXT_MODEL_API_KEY=none
# Realtime WebSocket bridge # Realtime WebSocket bridge
FUNASR_WEBSOCKET_IP=ws://fastapi-wss:10095/websocket_offline FUNASR_WEBSOCKET_IP=ws://fastapi-wss:10095/websocket_offline
TARGET_WS_URL=ws://funasr-online:10096 TARGET_WS_URL=ws://funasr-runtime-2pass:10095
REDIS_URL=redis://redis:6379/0 # Optional A/B target for the official C++/ONNX FunASR runtime service:
# TARGET_WS_URL=ws://funasr-online:10096
REDIS_URL=redis://redis:6379/0
LLM_API_KEY=none LLM_API_KEY=none
LLM_BASE_URL=http://192.168.0.46:59800/v1 LLM_BASE_URL=http://192.168.0.46:59800/v1
LLM_MODEL=46-qwen3.5-35B LLM_MODEL=46-qwen3.5-35B

View File

@ -94,9 +94,9 @@ services:
- "1" - "1"
funasr-online: funasr-online:
image: paraformer-asr:latest image: paraformer-asr:latest
container_name: funasr-online container_name: funasr-online
restart: unless-stopped restart: unless-stopped
runtime: nvidia runtime: nvidia
gpus: gpus:
@ -153,23 +153,53 @@ services:
- --keyfile - --keyfile
- "" - ""
funasr-runtime-2pass:
image: registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.13
container_name: funasr-runtime-2pass
restart: unless-stopped
working_dir: /workspace/FunASR/runtime
volumes:
- ./deploy/data/funasr-runtime-models:/workspace/models
ports:
- "10097:10095"
command:
- bash
- -lc
- >
touch /workspace/models/hotwords.txt &&
exec /workspace/FunASR/runtime/websocket/build/bin/funasr-wss-server-2pass
--download-model-dir /workspace/models
--vad-dir damo/speech_fsmn_vad_zh-cn-16k-common-onnx
--model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-onnx
--online-model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx
--punc-dir damo/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx
--lm-dir damo/speech_ngram_lm_zh-cn-ai-wesp-fst
--itn-dir thuduj12/fst_itn_zh
--decoder-thread-num 4
--model-thread-num 1
--io-thread-num 1
--port 10095
--hotword /workspace/models/hotwords.txt
--certfile ""
--keyfile ""
fastapi-wss: fastapi-wss:
image: fastapi-wss:runtime image: fastapi-wss:runtime
container_name: fastapi-wss container_name: fastapi-wss
restart: unless-stopped restart: unless-stopped
working_dir: /app/src working_dir: /app/src
env_file: env_file:
- ./config.env - ./config.env
environment: environment:
TARGET_WS_URL: ws://funasr-online:10096 TARGET_WS_URL: ws://funasr-runtime-2pass:10095
REDIS_URL: redis://redis:6379/0 REDIS_URL: redis://redis:6379/0
volumes: volumes:
- ./fastapi_wss:/app - ./fastapi_wss:/app
ports: ports:
- "10095:10095" - "10095:10095"
depends_on: depends_on:
- funasr-online - funasr-runtime-2pass
- redis - redis
command: command:
- python - python
- a2a_wss.py - a2a_wss.py

View File

@ -19,6 +19,7 @@ import os
import websockets import websockets
import re import re
import logging import logging
from contextlib import asynccontextmanager
from pathlib import Path from pathlib import Path
from typing import List, Optional, Dict, Set from typing import List, Optional, Dict, Set
from dataclasses import dataclass from dataclasses import dataclass
@ -789,6 +790,33 @@ def build_role_enriched_message(speaker_msg: Dict, role_result: Dict) -> Dict:
# 5. WebSocket 处理与任务追踪 # 5. WebSocket 处理与任务追踪
# ======================== # ========================
@asynccontextmanager
async def connect_target_asr():
connect_options = {
"close_timeout": 10,
"ping_interval": None,
"ping_timeout": None,
"max_queue": 1024,
}
try:
target_ws = await websockets.connect(
TARGET_WS_URL,
**connect_options,
subprotocols=["binary"],
)
except websockets.exceptions.InvalidMessage as exc:
logger.warning(
"ASR WebSocket binary subprotocol handshake failed, retrying without subprotocol: %s",
exc,
)
target_ws = await websockets.connect(TARGET_WS_URL, **connect_options)
try:
yield target_ws
finally:
await target_ws.close()
async def forward_audio(websocket): async def forward_audio(websocket):
""" """
WebSocket 连接处理函数核心入口 WebSocket 连接处理函数核心入口
@ -824,14 +852,7 @@ async def forward_audio(websocket):
try: try:
# 连接到目标 ASR 服务 # 连接到目标 ASR 服务
# 添加连接参数以提高鲁棒性 # 添加连接参数以提高鲁棒性
async with websockets.connect( async with connect_target_asr() as target_ws:
TARGET_WS_URL,
close_timeout=10, # 关闭超时
ping_interval=None,
ping_timeout=None,
max_queue=1024, # 消息队列大小
subprotocols=["binary"],
) as target_ws:
logger.info(f"已连接到 ASR 服务: {TARGET_WS_URL}") logger.info(f"已连接到 ASR 服务: {TARGET_WS_URL}")
# 发送 ASR 配置双通道模式PCM 音频格式) # 发送 ASR 配置双通道模式PCM 音频格式)