diff --git a/README.md b/README.md index b369b0828..fb8aceee0 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,7 @@ asset is published. ## What's new +- 2026/07/24: **v1.3.27 on PyPI** — the OpenAI-compatible server now reports detected SenseVoice language metadata in `verbose_json` and reuses the cached Fun-ASR-Nano `AutoModel` after vLLM fallback. When vLLM/VAD setup and its fallback both fail, half-initialized engine state is cleared so a later request can retry. Install with `python -m pip install -U "funasr==1.3.27"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.3.27) - 2026/07/23: **llama.cpp runtime v0.1.9** — adds `funasr-llamacpp-windows-x64-vulkan.zip` for standalone SenseVoiceSmall Vulkan inference on Windows with AMD, Intel, or NVIDIA drivers. Linux Vulkan, Windows CUDA, CPU/AVX2, Linux arm64, and macOS arm64 assets remain available. [Release ->](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.1.9) - 2026/07/23: **v1.3.26 on PyPI** — `funasr-server --model fun-asr-nano --hub ms` now honors the requested ModelScope hub for the default Fun-ASR-Nano model in both the vLLM path and the AutoModel fallback, avoiding unintended Hugging Face downloads when users choose ModelScope. Install with `python -m pip install -U "funasr==1.3.26"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.3.26) - 2026/07/23: **v1.3.25 on PyPI** — realtime WebSocket users can now use deterministic final-text hotword corrections with `POSTPROCESS_HOTWORDS:wrong=>right` or `--postprocess-hotword-file`, keeping fixed-name cleanup separate from model-level `HOTWORDS:` decoding bias. The source-tree realtime entrypoint also works without preinstalling the package. Install with `python -m pip install -U "funasr==1.3.25"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.3.25) diff --git a/README_ja.md b/README_ja.md index 739b8a0bd..2d9b7a709 100644 --- a/README_ja.md +++ b/README_ja.md @@ -99,6 +99,7 @@ Whisper は単一モデルですが、**FunASR はツールキット**です— ## 最新情報 +- 2026/07/24:**v1.3.27 が PyPI に公開** — OpenAI 互換サーバーが `verbose_json` で SenseVoice の検出言語を返し、vLLM fallback 後はキャッシュ済み Fun-ASR-Nano `AutoModel` を再利用します。vLLM/VAD 初期化と fallback の両方が失敗した場合は、半初期化状態を残さず、後続リクエストで再試行できます。インストール:`python -m pip install -U "funasr==1.3.27"`。[Release →](https://github.com/modelscope/FunASR/releases/tag/v1.3.27) - 2026/07/23:**v1.3.26 が PyPI に公開** — `funasr-server --model fun-asr-nano --hub ms` は ModelScope hub 指定を vLLM 経路と AutoModel fallback の両方で尊重します。インストール:`python -m pip install -U "funasr==1.3.26"`。[Release →](https://github.com/modelscope/FunASR/releases/tag/v1.3.26) - 2026/07/23:**llama.cpp runtime v0.1.9** — Windows Vulkan 向け `funasr-llamacpp-windows-x64-vulkan.zip` を追加。現在の AMD、Intel、NVIDIA Vulkan ドライバーで SenseVoiceSmall を単独実行できます。Linux Vulkan、Windows CUDA、CPU/AVX2、Linux arm64、macOS arm64 も引き続き提供します。[Release →](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.1.9) - 2026/07/22:**llama.cpp runtime v0.1.8** — Linux Vulkan tarball と Windows CUDA zip を含む CPU/エッジ向け GGUF ランタイム。ダウンロードと手順:[funasr.com/llama-cpp](https://www.funasr.com/llama-cpp.html) · [Release →](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.1.8) diff --git a/README_ko.md b/README_ko.md index 0037a23cc..152d74fd6 100644 --- a/README_ko.md +++ b/README_ko.md @@ -99,6 +99,7 @@ Whisper는 단일 모델이지만, **FunASR는 툴킷**입니다. 용도에 맞 ## 최신 소식 +- 2026/07/24: **v1.3.27 PyPI 공개** — OpenAI 호환 서버가 `verbose_json`에 SenseVoice 감지 언어를 반환하고, vLLM fallback 후에는 캐시된 Fun-ASR-Nano `AutoModel`을 재사용합니다. vLLM/VAD 초기화와 fallback이 모두 실패하면 반쯤 초기화된 상태를 남기지 않아 이후 요청에서 다시 시도할 수 있습니다. 설치: `python -m pip install -U "funasr==1.3.27"`. [Release →](https://github.com/modelscope/FunASR/releases/tag/v1.3.27) - 2026/07/23: **v1.3.26 PyPI 공개** — `funasr-server --model fun-asr-nano --hub ms`가 vLLM 경로와 AutoModel fallback 모두에서 ModelScope hub 선택을 존중합니다. 설치: `python -m pip install -U "funasr==1.3.26"`. [Release →](https://github.com/modelscope/FunASR/releases/tag/v1.3.26) - 2026/07/23: **llama.cpp runtime v0.1.9** — Windows Vulkan용 `funasr-llamacpp-windows-x64-vulkan.zip`을 추가했습니다. 최신 AMD, Intel 또는 NVIDIA Vulkan 드라이버에서 SenseVoiceSmall을 독립 실행할 수 있습니다. Linux Vulkan, Windows CUDA, CPU/AVX2, Linux arm64, macOS arm64 패키지도 계속 제공합니다. [Release →](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.1.9) - 2026/07/22: **llama.cpp runtime v0.1.8** — Linux Vulkan tarball과 Windows CUDA zip을 포함한 CPU/엣지용 GGUF 런타임입니다. 다운로드와 빠른 시작: [funasr.com/llama-cpp](https://www.funasr.com/llama-cpp.html) · [Release →](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.1.8) diff --git a/README_zh.md b/README_zh.md index 66c0b9f77..f3ced21b2 100644 --- a/README_zh.md +++ b/README_zh.md @@ -147,6 +147,7 @@ Whisper 是单个模型,**FunASR 是一个工具箱**——按场景挑模型 ## 最新动态 +- 2026/07/24:**v1.3.27 已发布到 PyPI** — OpenAI 兼容服务现在会在 `verbose_json` 中返回 SenseVoice 检测到的语言,并在 vLLM 降级后复用已缓存的 Fun-ASR-Nano `AutoModel`。当 vLLM/VAD 初始化及其 fallback 均失败时,不会残留半初始化的 engine 状态,后续请求可以重试。安装命令:`python -m pip install -U "funasr==1.3.27"`。[发布页 →](https://github.com/modelscope/FunASR/releases/tag/v1.3.27) - 2026/07/23:**llama.cpp runtime v0.1.9** — 新增独立的 Windows Vulkan 包 `funasr-llamacpp-windows-x64-vulkan.zip`,支持在安装当前 AMD、Intel 或 NVIDIA Vulkan 驱动的 Windows 机器上运行 SenseVoiceSmall;Linux Vulkan、Windows CUDA、CPU/AVX2、Linux arm64 和 macOS arm64 包继续提供。[发布页 →](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.1.9) - 2026/07/23:**v1.3.26 已发布到 PyPI** — `funasr-server --model fun-asr-nano --hub ms` 现在会在默认 Fun-ASR-Nano 的 vLLM 路径和 AutoModel fallback 路径中都尊重 ModelScope hub 选择,避免用户指定 ModelScope 时仍误走 Hugging Face 下载。安装命令:`python -m pip install -U "funasr==1.3.26"`。[发布页 →](https://github.com/modelscope/FunASR/releases/tag/v1.3.26) - 2026/07/23:**v1.3.25 已发布到 PyPI** — 实时 WebSocket 服务新增 `POSTPROCESS_HOTWORDS:错词=>正确词` 与 `--postprocess-hotword-file`,可在 final 文本阶段做确定性热词纠正,避免把固定错词修正误用成模型层 `HOTWORDS:` 解码偏置;源码目录下的实时服务入口也可直接运行。安装命令:`python -m pip install -U "funasr==1.3.25"`。[发布页 →](https://github.com/modelscope/FunASR/releases/tag/v1.3.25) diff --git a/funasr/bin/_server_app.py b/funasr/bin/_server_app.py index 3a7e47808..94e2ba266 100644 --- a/funasr/bin/_server_app.py +++ b/funasr/bin/_server_app.py @@ -10,6 +10,7 @@ import re import time import logging import tempfile +from pathlib import Path from typing import Optional import numpy as np @@ -26,6 +27,9 @@ except ImportError: logger = logging.getLogger("funasr.server") +PACKAGE_VERSION = (Path(__file__).resolve().parents[1] / "version.txt").read_text().strip() + + _LANGUAGE_TAG_RE = re.compile(r"<\|(zh|en|yue|ja|ko)\|>") @@ -126,7 +130,7 @@ def create_app(device: str = "cuda", preload_model: str = "auto", model_path: st if preload_model == "auto": preload_model = "fun-asr-nano" if device.startswith("cuda") else "sensevoice" - app = FastAPI(title="FunASR Server", version="1.3.6") + app = FastAPI(title="FunASR Server", version=PACKAGE_VERSION) app.state.device = device app.state.engine = None app.state.vad_model = None diff --git a/funasr/bin/server.py b/funasr/bin/server.py index 083863102..e55d75f28 100644 --- a/funasr/bin/server.py +++ b/funasr/bin/server.py @@ -11,6 +11,14 @@ Usage: import argparse import sys +from pathlib import Path + + +PACKAGE_VERSION = (Path(__file__).resolve().parents[1] / "version.txt").read_text().strip() + + +def server_version_label(): + return f"FunASR Server v{PACKAGE_VERSION}" def main(): @@ -58,7 +66,7 @@ Then use with OpenAI SDK: app = create_app(device=args.device, preload_model=args.model, model_path=args.model_path, hub=args.hub) print(f"╔══════════════════════════════════════════════╗") - print(f"║ FunASR Server v1.3.6 ║") + print(f"║ {server_version_label():<44}║") print(f"║ Device: {args.device:<8} ║") print(f"║ Model: {args.model:<12} ║") if args.model_path: diff --git a/funasr/version.txt b/funasr/version.txt index 51ce721b6..10e70bde7 100644 --- a/funasr/version.txt +++ b/funasr/version.txt @@ -1 +1 @@ -1.3.26 +1.3.27 diff --git a/tests/test_server_app_openai_segments.py b/tests/test_server_app_openai_segments.py index 3e3e3eb4d..4a735de3b 100644 --- a/tests/test_server_app_openai_segments.py +++ b/tests/test_server_app_openai_segments.py @@ -9,6 +9,7 @@ import pytest REPO_ROOT = Path(__file__).resolve().parents[1] SERVICE_PATH = REPO_ROOT / "funasr" / "bin" / "_server_app.py" +SERVER_CLI_PATH = REPO_ROOT / "funasr" / "bin" / "server.py" def load_server_app(monkeypatch): @@ -16,6 +17,7 @@ def load_server_app(monkeypatch): def __init__(self, *args, **kwargs): self.state = types.SimpleNamespace() self.routes = {} + self.metadata = kwargs def post(self, path, *args, **kwargs): def decorator(func): @@ -53,7 +55,17 @@ def load_server_app(monkeypatch): return module -def install_dummy_funasr(monkeypatch, fail_once_for_models=()): +def load_server_cli(): + module_name = "funasr_server_cli_under_test" + sys.modules.pop(module_name, None) + spec = importlib.util.spec_from_file_location(module_name, SERVER_CLI_PATH) + module = importlib.util.module_from_spec(spec) + assert spec.loader is not None + spec.loader.exec_module(module) + return module + + +def install_dummy_funasr(monkeypatch, fail_once_for_models=(), generated_text="transcript"): remaining_failures = {model: 1 for model in fail_once_for_models} class DummyAutoModel: @@ -72,7 +84,7 @@ def install_dummy_funasr(monkeypatch, fail_once_for_models=()): def generate(self, **kwargs): if self.kwargs.get("model") == "fsmn-vad": return [{"value": [[0, 1000]]}] - return [{"text": "transcript"}] + return [{"text": generated_text}] funasr_stub = types.ModuleType("funasr") funasr_stub.AutoModel = DummyAutoModel @@ -174,6 +186,46 @@ def test_resolve_transcription_language_does_not_default_to_chinese(monkeypatch) assert module.resolve_transcription_language(None, {}) != "zh" +def test_verbose_json_reports_sensevoice_detected_language(monkeypatch): + module = load_server_app(monkeypatch) + install_dummy_funasr( + monkeypatch, + generated_text="<|en|><|NEUTRAL|><|Speech|>hello", + ) + monkeypatch.setattr(module.sf, "info", lambda path: types.SimpleNamespace(duration=1.25)) + app = module.create_app(device="cpu", preload_model="sensevoice") + transcribe = app.routes[("POST", "/v1/audio/transcriptions")] + + response = asyncio.run( + transcribe( + file=DummyUpload(), + model="sensevoice", + language=None, + response_format="verbose_json", + spk=False, + ) + ) + + assert response["language"] == "en" + assert response["text"] == "hello" + assert response["segments"] == [ + {"id": 0, "start": 0.0, "end": 1.25, "text": "hello", "words": []} + ] + + +def test_server_versions_follow_package_version(monkeypatch): + expected = (REPO_ROOT / "funasr" / "version.txt").read_text().strip() + module = load_server_app(monkeypatch) + server_module = load_server_cli() + install_dummy_funasr(monkeypatch) + + app = module.create_app(device="cpu", preload_model="sensevoice") + + assert app.metadata["version"] == expected + assert hasattr(server_module, "server_version_label") + assert server_module.server_version_label() == f"FunASR Server v{expected}" + + def test_default_fun_asr_nano_uses_requested_modelscope_hub(monkeypatch): module = load_server_app(monkeypatch) DummyAutoModel = install_dummy_funasr(monkeypatch)