refactor: organize backend and frontend directories
parent
13278d3aea
commit
24b4b62fa9
|
|
@ -18,7 +18,7 @@ FUNASR_FINALIZE_TIMEOUT_SECONDS=300
|
|||
FUNASR_NATIVE_WS_HOST=127.0.0.1
|
||||
FUNASR_NATIVE_WS_PORT=10095
|
||||
|
||||
# CAM++ is required and started by scripts/run_backend.py.
|
||||
# CAM++ is required and started by backend/run_backend.py.
|
||||
AUXILIARY_SERVICE_URL=http://127.0.0.1:8010
|
||||
AUXILIARY_DEVICE=cpu
|
||||
AUXILIARY_PRELOAD_KINDS=speaker_verification
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ If directories have different names, set `FUNASR_ASR_MODEL` and `FUNASR_VAD_MODE
|
|||
Install a torch/torchaudio build suitable for the host, then install the project dependencies and models:
|
||||
|
||||
~~~powershell
|
||||
python -m pip install -r requirements-funasr.txt
|
||||
python -m pip install -r requirements-auxiliary.txt
|
||||
python -m pip install -r backend/requirements-funasr.txt
|
||||
python -m pip install -r backend/requirements-auxiliary.txt
|
||||
python scripts/download_models.py --funasr-runtime
|
||||
if (-not (Test-Path .env)) { Copy-Item .env.funasr.example .env }
|
||||
~~~
|
||||
|
|
@ -32,8 +32,8 @@ if (-not (Test-Path .env)) { Copy-Item .env.funasr.example .env }
|
|||
Start the backend and frontend in separate terminals:
|
||||
|
||||
~~~powershell
|
||||
python scripts\run_backend.py
|
||||
python scripts\run_frontend.py
|
||||
python backend\run_backend.py
|
||||
python frontend\run_frontend.py
|
||||
~~~
|
||||
|
||||
Defaults are frontend port 8080, browser backend port 8082, CAM++ HTTP port 8010, and native FunASR WS port 10095 bound to loopback. Change `FRONTEND_PORT`, `WEB_PORT`, `AUXILIARY_SERVICE_URL`, `FUNASR_NATIVE_WS_HOST`, and `FUNASR_NATIVE_WS_PORT` together when needed. `BACKEND_INTERNAL_URL` is the backend origin reachable from the frontend process; it defaults to `http://127.0.0.1:${WEB_PORT}`.
|
||||
|
|
|
|||
31
README.md
31
README.md
|
|
@ -1,25 +1,32 @@
|
|||
# FunASR realtime ASR demo
|
||||
|
||||
This branch runs FunASR's native online WebSocket flow with local streaming ASR and FSMN VAD models. CAM++ speaker embeddings are handled by the separate auxiliary service. The browser page, JavaScript, and CSS are copied byte-for-byte from the local `tencent-demo/static` directory.
|
||||
This project keeps the frontend and backend in separate directories. The frontend serves the original Tencent demo assets from frontend/static/ unchanged.
|
||||
|
||||
## Start
|
||||
## Project layout
|
||||
|
||||
Install a torch/torchaudio build for the host, then install `requirements-funasr.txt` and `requirements-auxiliary.txt`. Copy `.env.funasr.example` to `.env` if no `.env` exists, then download the three required FunASR assets:
|
||||
- backend/: FunASR WebSocket adapter, native engine launcher, CAM++ service, and backend dependencies.
|
||||
- frontend/: Tencent demo files, static HTTP/WebSocket proxy, and frontend dependencies.
|
||||
- scripts/: shared model download tools and the model manifest.
|
||||
|
||||
## Install and download models
|
||||
|
||||
Install a host-compatible PyTorch/torchaudio build first. Then install backend dependencies, copy the example environment file, and download the FunASR runtime models:
|
||||
|
||||
~~~powershell
|
||||
python -m pip install -r backend/requirements-funasr.txt
|
||||
python -m pip install -r backend/requirements-auxiliary.txt
|
||||
python -m pip install -r frontend/requirements.txt
|
||||
if (-not (Test-Path .env)) { Copy-Item .env.funasr.example .env }
|
||||
python scripts/download_models.py --funasr-runtime
|
||||
~~~
|
||||
|
||||
Start the backend (CAM++, native FunASR WSS, and the browser protocol bridge) in one terminal:
|
||||
## Start
|
||||
|
||||
Run the backend and frontend in separate terminals from the project root:
|
||||
|
||||
~~~powershell
|
||||
python scripts\run_backend.py
|
||||
python backend/run_backend.py
|
||||
python frontend/run_frontend.py
|
||||
~~~
|
||||
|
||||
Start the frontend in another terminal:
|
||||
|
||||
~~~powershell
|
||||
python scripts\run_frontend.py
|
||||
~~~
|
||||
|
||||
Open the URL configured by `FRONTEND_HOST` and `FRONTEND_PORT`. The frontend keeps the Tencent demo's same-origin `/ws` and `/api/stop` calls and proxies them to `BACKEND_INTERNAL_URL`. The public backend bridge defaults to port 8082; the native FunASR WS socket defaults to loopback port 10095. Model paths and device settings are described in [FUNASR_README.md](FUNASR_README.md).
|
||||
The backend supervises CAM++, FunASR native WSS, and the browser protocol adapter. The frontend serves the unchanged Tencent page and proxies its same-origin /ws and /api/stop requests to the backend. Configure ports and model locations in .env.
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Qwen/Qwen3-ASR-1.7B
|
|||
cd D:\github-project\ASR\Qwen-Asr\demo
|
||||
python -m venv .venv
|
||||
.\.venv\Scripts\Activate.ps1
|
||||
pip install -r requirements-download.txt
|
||||
pip install modelscope==1.34.0
|
||||
python scripts\download_models.py
|
||||
```
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ python scripts\download_models.py
|
|||
需要宿主机具备与 VLLM 兼容的 Python、CUDA 和 NVIDIA 驱动环境。安装部署依赖:
|
||||
|
||||
```bash
|
||||
python -m pip install -r requirements-deploy.txt
|
||||
python -m pip install -r backend/requirements-qwen-legacy.txt
|
||||
```
|
||||
|
||||
先复制并按服务器实际路径修改 `.env`,启动器会自动读取该文件:
|
||||
|
|
@ -82,24 +82,24 @@ cp .env.example .env
|
|||
默认启动 `Qwen/Qwen3-ASR-0.6B`,监听地址为 `0.0.0.0:9950`:
|
||||
|
||||
```bash
|
||||
python scripts/serve.py
|
||||
python backend/serve_qwen_legacy.py
|
||||
```
|
||||
|
||||
模型和启动检查循环可以通过命令行或环境变量传入;端口统一在 `scripts/serve.py` 的 `SERVER_PORT` 变量中维护:
|
||||
模型和启动检查循环可以通过命令行或环境变量传入;端口统一在 `backend/serve_qwen_legacy.py` 的 `SERVER_PORT` 变量中维护:
|
||||
|
||||
```bash
|
||||
QWEN3_ASR_MODEL=0.6b VLLM_STARTUP_CHECK_LOOPS=120 \
|
||||
VLLM_STARTUP_CHECK_INTERVAL_SECONDS=2 python scripts/serve.py
|
||||
VLLM_STARTUP_CHECK_INTERVAL_SECONDS=2 python backend/serve_qwen_legacy.py
|
||||
```
|
||||
|
||||
如果使用 `1.7B`,下载和启动必须指定同一个模型:
|
||||
|
||||
```powershell
|
||||
python scripts\download_models.py --model 1.7b
|
||||
python -m scripts.serve --model 1.7b
|
||||
python -m backend.serve_qwen_legacy --model 1.7b
|
||||
```
|
||||
|
||||
启动器会按 `VLLM_STARTUP_CHECK_LOOPS` 次数轮询 `/health`,每次间隔由 `VLLM_STARTUP_CHECK_INTERVAL_SECONDS` 指定。服务端口、健康检查端口和就绪提示统一使用 `scripts/serve.py` 中的 `SERVER_PORT`。
|
||||
启动器会按 `VLLM_STARTUP_CHECK_LOOPS` 次数轮询 `/health`,每次间隔由 `VLLM_STARTUP_CHECK_INTERVAL_SECONDS` 指定。服务端口、健康检查端口和就绪提示统一使用 `backend/serve_qwen_legacy.py` 中的 `SERVER_PORT`。
|
||||
|
||||
服务启动后可检查:
|
||||
|
||||
|
|
@ -125,8 +125,8 @@ curl "http://${VLLM_DISPLAY_HOST:-127.0.0.1}:9950/v1/audio/transcriptions" \
|
|||
|
||||
```powershell
|
||||
cd D:\github-project\ASR\Qwen-Asr\demo
|
||||
pip install -r requirements-auxiliary.txt
|
||||
python scripts\auxiliary_server.py
|
||||
pip install -r backend/requirements-auxiliary.txt
|
||||
python -m backend.auxiliary_server
|
||||
```
|
||||
|
||||
辅助服务默认监听 `0.0.0.0:8010`。实时链路启动时严格加载 VAD 和 CAM++ `speaker_verification` 声纹模型,用于每个 turn 的特征提取与在线聚类;完整 CAM++ 分离、Transformer 和 ForcedAligner 不阻断核心服务,完整分离模型会在调用 `/v1/diarization` 时按需加载。检查状态:
|
||||
|
|
@ -144,8 +144,8 @@ WebSocket demo 默认连接 `9950` 的 ASR VLLM,辅助服务使用 `8010`。`/
|
|||
## 项目边界
|
||||
|
||||
- `scripts/download_models.py`:下载选定 ASR 和全部辅助模型资产。
|
||||
- `scripts/serve.py`:读取 `.env`,解析模型选择、宿主机参数并启动新版 `vllm serve`。
|
||||
- `requirements-deploy.txt`:安装宿主机部署所需的官方 Qwen3-ASR VLLM 依赖。
|
||||
- `backend/serve_qwen_legacy.py`:读取 `.env`,解析模型选择、宿主机参数并启动新版 `vllm serve`。
|
||||
- `backend/requirements-qwen-legacy.txt`:安装宿主机部署所需的官方 Qwen3-ASR VLLM 依赖。
|
||||
- `tests/`:只验证本项目自己的模型清单和选择逻辑,不依赖原项目。
|
||||
|
||||
模型服务就绪后,新的实时 ASR demo 放在同级 `demo` 项目中继续开发,但不得通过 Python import 或 HTTP/WebSocket 调用原项目服务。
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
"""Backend services and the realtime ASR protocol adapter."""
|
||||
|
|
@ -6,6 +6,7 @@ from __future__ import annotations
|
|||
import asyncio
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
from collections.abc import Mapping
|
||||
|
|
@ -16,16 +17,17 @@ from aiohttp import web
|
|||
from aiohttp.web_request import FileField
|
||||
from dotenv import load_dotenv
|
||||
|
||||
try:
|
||||
from .model_manifest import auxiliary_models, load_manifest, model_directory
|
||||
except ImportError:
|
||||
from model_manifest import auxiliary_models, load_manifest, model_directory
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
# Support direct script startup while sharing the root model manifest.
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from scripts.model_manifest import auxiliary_models, load_manifest, model_directory
|
||||
|
||||
|
||||
# 将辅助服务端口固定在代码变量中,服务器启动时只需执行脚本,便于部署和排查。
|
||||
AUXILIARY_HOST = "0.0.0.0"
|
||||
AUXILIARY_PORT = 8010
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
# 独立启动辅助服务也必须读取部署配置,不能只在启动 vLLM 时才加载 .env。
|
||||
load_dotenv(PROJECT_ROOT / ".env")
|
||||
MODELS_DIR = Path(os.getenv("MODEL_DIR", str(PROJECT_ROOT / "models"))).resolve()
|
||||
|
|
@ -31,15 +31,15 @@ VAD、CAM++ 声纹模型及在线聚类,WebSocket 只做音频流编排,不
|
|||
```powershell
|
||||
cd D:\github-project\ASR\Qwen-Asr\demo
|
||||
python scripts\download_models.py
|
||||
python scripts\serve.py
|
||||
python backend/serve_qwen_legacy.py
|
||||
```
|
||||
|
||||
另一个终端启动辅助模型服务:
|
||||
|
||||
```powershell
|
||||
cd D:\github-project\ASR\Qwen-Asr\demo
|
||||
pip install -r requirements-auxiliary.txt
|
||||
python scripts\auxiliary_server.py
|
||||
pip install -r backend/requirements-auxiliary.txt
|
||||
python -m backend.auxiliary_server
|
||||
```
|
||||
|
||||
另开一个终端启动 WebSocket 页面:
|
||||
|
|
@ -78,7 +78,7 @@ python server.py --no-browser
|
|||
`pipeline([wav_path], output_emb=True)`。不能绕过 pipeline 预处理后直接调用
|
||||
`pipeline.model`,否则采样率、声道和 waveform 预处理不会执行,部分 ModelScope
|
||||
版本会直接抛异常,WebSocket 仍会继续输出 ASR 并把说话人保留为 pending。
|
||||
更新辅助服务代码后需要重启 `python scripts/auxiliary_server.py`,仅重启页面
|
||||
更新辅助服务代码后需要重启 `python -m backend.auxiliary_server`,仅重启页面
|
||||
服务不会替换已经驻留在 GPU 中的旧辅助服务进程。
|
||||
|
||||
## 页面操作
|
||||
|
|
@ -123,7 +123,7 @@ class FunASRModelService:
|
|||
from funasr import AutoModel
|
||||
except ImportError as exc: # pragma: no cover - deployment-only branch
|
||||
raise RuntimeError(
|
||||
"FunASR is not installed; run pip install -r requirements-deploy.txt"
|
||||
"FunASR is not installed; run pip install -r backend/requirements-funasr.txt"
|
||||
) from exc
|
||||
|
||||
def load_models() -> tuple[Any, Any]:
|
||||
|
|
@ -26,7 +26,7 @@ except ImportError:
|
|||
from auxiliary_service import AuxiliaryModelService, AuxiliaryServiceConfig
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
load_dotenv(PROJECT_ROOT / ".env")
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
WEB_HOST = os.getenv("WEB_HOST", "0.0.0.0")
|
||||
|
|
@ -25,7 +25,7 @@ from speaker_assembler import SegmentAssembler
|
|||
|
||||
|
||||
# 与部署启动器读取同一配置;外部环境变量优先于 demo/.env。
|
||||
DEPLOY_ROOT = Path(__file__).resolve().parents[1]
|
||||
DEPLOY_ROOT = Path(__file__).resolve().parents[2]
|
||||
load_dotenv(DEPLOY_ROOT / ".env")
|
||||
|
||||
# 监听所有网卡,允许同一局域网内的浏览器访问服务器上的 Demo;端口集中在代码
|
||||
|
|
@ -7,7 +7,7 @@ import unittest
|
|||
from unittest.mock import patch
|
||||
|
||||
try:
|
||||
from realtime_websocket.funasr_engine import (
|
||||
from backend.realtime_websocket.funasr_engine import (
|
||||
FunASRRealtimeSession,
|
||||
FunASRServiceConfig,
|
||||
_result_text,
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
"""Contract test for the unchanged Tencent UI to FunASR native WS bridge."""
|
||||
"""Contract test for the unchanged Tencent UI to FunASR native WS bridge."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ from unittest.mock import patch
|
|||
from aiohttp import web
|
||||
from aiohttp.test_utils import AioHTTPTestCase
|
||||
|
||||
from realtime_websocket.funasr_server import (
|
||||
from backend.realtime_websocket.funasr_server import (
|
||||
AUXILIARY_KEY,
|
||||
SESSION_REGISTRY_KEY,
|
||||
websocket_handler,
|
||||
|
|
@ -78,7 +78,7 @@ class FunASRBridgeTests(AioHTTPTestCase):
|
|||
async def test_tencent_ui_messages_use_native_funasr_and_keep_speaker_label(self):
|
||||
native = FakeNativeWebSocket()
|
||||
with patch(
|
||||
"realtime_websocket.funasr_server.websocket_connect",
|
||||
"backend.realtime_websocket.funasr_server.websocket_connect",
|
||||
return_value=native,
|
||||
):
|
||||
ws = await self.client.ws_connect("/ws")
|
||||
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
import unittest
|
||||
|
||||
try:
|
||||
from realtime_websocket.speaker_assembler import SegmentAssembler
|
||||
from backend.realtime_websocket.speaker_assembler import SegmentAssembler
|
||||
except ModuleNotFoundError:
|
||||
from speaker_assembler import SegmentAssembler
|
||||
|
||||
|
|
@ -191,7 +191,7 @@ def main() -> None:
|
|||
websocket = None
|
||||
try:
|
||||
auxiliary = subprocess.Popen(
|
||||
[sys.executable, "-m", "scripts.auxiliary_server"],
|
||||
[sys.executable, "-m", "backend.auxiliary_server"],
|
||||
cwd=PROJECT_ROOT,
|
||||
env=env,
|
||||
)
|
||||
|
|
@ -199,7 +199,7 @@ def main() -> None:
|
|||
|
||||
native_args = [
|
||||
sys.executable,
|
||||
str(PROJECT_ROOT / "realtime_websocket" / "funasr_native_wss.py"),
|
||||
str(PROJECT_ROOT / "backend" / "realtime_websocket" / "funasr_native_wss.py"),
|
||||
"--host",
|
||||
native_host,
|
||||
"--port",
|
||||
|
|
@ -230,7 +230,7 @@ def main() -> None:
|
|||
wait_for_tcp(probe_host, native_port, native)
|
||||
|
||||
websocket = subprocess.Popen(
|
||||
[sys.executable, "-m", "scripts.run_funasr_demo", "--no-browser"],
|
||||
[sys.executable, "-m", "backend.run_funasr_demo", "--no-browser"],
|
||||
cwd=PROJECT_ROOT,
|
||||
env=env,
|
||||
)
|
||||
|
|
@ -10,7 +10,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from realtime_websocket.funasr_server import main
|
||||
from backend.realtime_websocket.funasr_server import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
@ -19,16 +19,16 @@ from dotenv import load_dotenv
|
|||
|
||||
# 启动器自动读取 demo/.env;系统环境变量仍然优先,便于部署平台临时覆盖配置。
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
# Resolve the shared model manifest for direct script startup.
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
load_dotenv(PROJECT_ROOT / ".env")
|
||||
|
||||
# 将服务端口集中在代码变量中维护,启动时不需要额外传入端口参数;健康检查、
|
||||
# VLLM 子进程命令和就绪提示都使用同一个端口,避免配置不一致导致误判。
|
||||
SERVER_PORT = int(os.getenv("VLLM_PORT", "9950"))
|
||||
|
||||
try:
|
||||
from .model_manifest import load_manifest, model_directory, resolve_model_id
|
||||
except ImportError:
|
||||
from model_manifest import load_manifest, model_directory, resolve_model_id
|
||||
from scripts.model_manifest import load_manifest, model_directory, resolve_model_id
|
||||
|
||||
|
||||
def has_model_weights(model_path: Path) -> bool:
|
||||
|
|
@ -125,7 +125,7 @@ def build_server_command(args: argparse.Namespace, model_id: str, model_path: Pa
|
|||
executable_name = os.getenv("VLLM_EXECUTABLE", "vllm")
|
||||
executable = shutil.which(executable_name)
|
||||
if executable is None:
|
||||
raise RuntimeError(f"{executable_name} was not found; install requirements-deploy.txt first")
|
||||
raise RuntimeError(f"{executable_name} was not found; install backend/requirements-qwen-legacy.txt first")
|
||||
|
||||
served_model_name = args.served_model_name or model_id
|
||||
command = [
|
||||
|
|
@ -39,7 +39,7 @@ class ModelManifestTests(unittest.TestCase):
|
|||
self.assertIn("Qwen/Qwen3-ForcedAligner-0.6B", assets)
|
||||
|
||||
def test_model_directory_is_under_demo_models(self) -> None:
|
||||
models_dir = Path(__file__).resolve().parents[1] / "models"
|
||||
models_dir = Path(__file__).resolve().parents[2] / "models"
|
||||
for model_id in [*self.manifest["models"], *auxiliary_models(self.manifest)]:
|
||||
self.assertTrue(model_directory(model_id, self.manifest, models_dir).is_relative_to(models_dir))
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ import unittest
|
|||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from scripts.serve import SERVER_PORT, build_parser, build_server_command
|
||||
from backend.serve_qwen_legacy import SERVER_PORT, build_parser, build_server_command
|
||||
|
||||
|
||||
class ServeConfigTests(unittest.TestCase):
|
||||
|
|
@ -29,7 +29,7 @@ class ServeConfigTests(unittest.TestCase):
|
|||
self.assertEqual(args.startup_check_loops, 12)
|
||||
self.assertEqual(args.startup_check_interval, 0.5)
|
||||
|
||||
@patch("scripts.serve.shutil.which", return_value="/opt/asr-gb10/bin/vllm")
|
||||
@patch("backend.serve_qwen_legacy.shutil.which", return_value="/opt/asr-gb10/bin/vllm")
|
||||
def test_builds_native_vllm_serve_command(self, _which: object) -> None:
|
||||
"""启动器应生成已验证的新版 vllm serve 命令。"""
|
||||
args = build_parser().parse_args([])
|
||||
|
|
@ -7,7 +7,7 @@ import numpy as np
|
|||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
from scripts.auxiliary_server import (
|
||||
from backend.auxiliary_server import (
|
||||
AuxiliaryRuntime,
|
||||
_coerce_finite_float,
|
||||
_normalize_diarization_segments,
|
||||
|
|
@ -42,8 +42,8 @@ class AuxiliaryServerTests(unittest.TestCase):
|
|||
"aligner": {"kind": "forced_aligner"},
|
||||
}
|
||||
loaded_kinds = []
|
||||
with patch("scripts.auxiliary_server._asset_ready", return_value=True), \
|
||||
patch("scripts.auxiliary_server.model_directory", return_value=runtime.manifest and SimpleNamespace()), \
|
||||
with patch("backend.auxiliary_server._asset_ready", return_value=True), \
|
||||
patch("backend.auxiliary_server.model_directory", return_value=runtime.manifest and SimpleNamespace()), \
|
||||
patch.object(runtime, "_load_asset", side_effect=lambda model_id, config, path: loaded_kinds.append(config["kind"]) or object()):
|
||||
runtime.preload()
|
||||
self.assertEqual(loaded_kinds, ["vad"])
|
||||
|
|
@ -57,8 +57,8 @@ class AuxiliaryServerTests(unittest.TestCase):
|
|||
"vad": {"kind": "vad"},
|
||||
"campplus": {"kind": "speaker_verification"},
|
||||
}
|
||||
with patch("scripts.auxiliary_server._asset_ready", side_effect=[True, False]), \
|
||||
patch("scripts.auxiliary_server.model_directory", return_value=SimpleNamespace()):
|
||||
with patch("backend.auxiliary_server._asset_ready", side_effect=[True, False]), \
|
||||
patch("backend.auxiliary_server.model_directory", return_value=SimpleNamespace()):
|
||||
with self.assertRaisesRegex(RuntimeError, "campplus"):
|
||||
runtime.preload()
|
||||
|
||||
|
|
@ -76,8 +76,8 @@ class AuxiliaryServerTests(unittest.TestCase):
|
|||
"""VAD 是核心依赖,缺失时错误必须给出可执行的修复方向。"""
|
||||
runtime = AuxiliaryRuntime()
|
||||
runtime.assets = {"vad": {"kind": "vad"}}
|
||||
with patch("scripts.auxiliary_server._asset_ready", return_value=False), \
|
||||
patch("scripts.auxiliary_server.model_directory", return_value=SimpleNamespace(__str__=lambda self: "/models/vad")):
|
||||
with patch("backend.auxiliary_server._asset_ready", return_value=False), \
|
||||
patch("backend.auxiliary_server.model_directory", return_value=SimpleNamespace(__str__=lambda self: "/models/vad")):
|
||||
with self.assertRaisesRegex(RuntimeError, "download_models.py --auxiliary-only"):
|
||||
runtime.preload()
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Dependencies for serving the unchanged Tencent demo UI.
|
||||
aiohttp==3.11.11
|
||||
python-dotenv>=1.0
|
||||
|
|
@ -13,7 +13,7 @@ from aiohttp import ClientSession, ClientTimeout, WSMsgType, web
|
|||
from dotenv import load_dotenv
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
STATIC_ROOT = PROJECT_ROOT / "realtime_websocket" / "static"
|
||||
STATIC_ROOT = Path(__file__).resolve().parent / "static"
|
||||
load_dotenv(PROJECT_ROOT / ".env")
|
||||
FRONTEND_HOST = os.getenv("FRONTEND_HOST", "127.0.0.1")
|
||||
FRONTEND_PORT = int(os.getenv("FRONTEND_PORT", "8080"))
|
||||
|
|
@ -18,8 +18,8 @@ dependencies = [
|
|||
]
|
||||
|
||||
[project.scripts]
|
||||
funasr-realtime-demo = "scripts.run_funasr_demo:main"
|
||||
funasr-realtime-demo = "backend.run_funasr_demo:main"
|
||||
funasr-download-models = "scripts.download_models:main"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["scripts", "realtime_websocket"]
|
||||
packages = ["scripts", "backend", "backend.realtime_websocket"]
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ dependencies = [
|
|||
|
||||
[project.scripts]
|
||||
qwen3-asr-download = "scripts.download_models:main"
|
||||
qwen3-asr-serve = "scripts.serve:main"
|
||||
qwen3-asr-serve = "backend.serve_qwen_legacy:main"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["scripts"]
|
||||
packages = ["scripts", "backend", "backend.realtime_websocket"]
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ def download_model(
|
|||
from modelscope.hub.snapshot_download import snapshot_download
|
||||
except ImportError as exc:
|
||||
raise RuntimeError(
|
||||
"ModelScope is required for downloading; install requirements-funasr.txt first"
|
||||
"ModelScope is required for downloading; install backend/requirements-funasr.txt first"
|
||||
) from exc
|
||||
|
||||
model_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
|
|
|||
Loading…
Reference in New Issue