Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ asset is published.

## What's new

- 2026/07/24: **v1.3.28 hotfix on PyPI** — realtime WebSocket finalization now preserves clean continuous partial transcripts when a VAD-locked decode truncates to a short prefix, repeats a hallucinated phrase, or raises; short STOP tails, VAD finalization, and speaker completion now share the same reliable path. SenseVoice subtitle segmentation also aligns rich tags, punctuation, and word/BPE timestamps without collapsing Chinese into one cue or damaging English surface text. Install with `python -m pip install -U "funasr==1.3.28"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.3.28)
- 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)
Expand Down
1 change: 1 addition & 0 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Whisper は単一モデルですが、**FunASR はツールキット**です—

## 最新情報

- 2026/07/24:**v1.3.28 hotfix が PyPI に公開** — VAD で確定した realtime WebSocket の最終結果が短い接頭辞、反復 hallucination、または decode 例外へ退化した場合、現在の音声区間を連続して完全に覆う clean partial を保持します。短い STOP tail、VAD finalize、話者処理も同じ信頼できる完了経路に統一しました。SenseVoice 字幕は rich tag、句読点、word/BPE timestamp を正しく整列し、中国語を一つの cue に潰さず、英語表記も保持します。インストール:`python -m pip install -U "funasr==1.3.28"`。[Release →](https://github.com/modelscope/FunASR/releases/tag/v1.3.28)
- 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)
Expand Down
1 change: 1 addition & 0 deletions README_ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Whisper는 단일 모델이지만, **FunASR는 툴킷**입니다. 용도에 맞

## 최신 소식

- 2026/07/24: **v1.3.28 hotfix PyPI 공개** — VAD로 확정된 realtime WebSocket 최종 결과가 짧은 접두사, 반복 hallucination 또는 decode 예외로 퇴화하면 현재 음성 구간을 연속해서 완전히 덮는 clean partial을 보존합니다. 짧은 STOP tail, VAD finalize, 화자 완료 처리도 동일한 안정적인 경로로 통합했습니다. SenseVoice 자막은 rich tag, 구두점, word/BPE timestamp를 올바르게 정렬해 중국어가 하나의 cue로 합쳐지거나 영어 원문이 손상되지 않습니다. 설치: `python -m pip install -U "funasr==1.3.28"`. [Release →](https://github.com/modelscope/FunASR/releases/tag/v1.3.28)
- 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)
Expand Down
1 change: 1 addition & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ Whisper 是单个模型,**FunASR 是一个工具箱**——按场景挑模型

## 最新动态

- 2026/07/24:**v1.3.28 热修复已发布到 PyPI** — 实时 WebSocket 在 VAD 锁句结果退化为短前缀、重复幻觉或解码异常时,会保留连续且完整覆盖当前语音段的干净 partial;短音频 STOP、VAD 收尾和说话人结束现在统一走可靠的完成路径。SenseVoice 字幕分句也会正确对齐富标签、标点与词/BPE 时间戳,不再把中文压成一个字幕块,也不会破坏英文原文。安装命令:`python -m pip install -U "funasr==1.3.28"`。[发布页 →](https://github.com/modelscope/FunASR/releases/tag/v1.3.28)
- 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)
Expand Down
3 changes: 2 additions & 1 deletion funasr/bin/realtime_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import warnings
import regex
import websockets
from websockets.exceptions import ConnectionClosed

from funasr.utils.postprocess_hotwords import (
apply_postprocess_hotwords_to_results,
Expand Down Expand Up @@ -1154,7 +1155,7 @@ async def handle_client(websocket, args):
await websocket.send(json.dumps(result))
last_decode_time = now

except websockets.exceptions.ConnectionClosed:
except ConnectionClosed:
logger.info("Client disconnected")
except Exception as e:
logger.error(f"Error: {e}", exc_info=True)
Expand Down
2 changes: 1 addition & 1 deletion funasr/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.3.27
1.3.28
71 changes: 64 additions & 7 deletions tests/test_realtime_ws_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,72 @@ def test_example_realtime_entrypoint_delegates_to_packaged_cli():


def load_service_module():
websockets_stub = types.SimpleNamespace(
exceptions=types.SimpleNamespace(ConnectionClosed=Exception),
serve=lambda *args, **kwargs: None,
)
sys.modules.setdefault("websockets", websockets_stub)
websockets_stub = types.ModuleType("websockets")
websockets_stub.serve = lambda *args, **kwargs: None
exceptions_stub = types.ModuleType("websockets.exceptions")
exceptions_stub.ConnectionClosed = type("ConnectionClosed", (Exception,), {})

previous_websockets = sys.modules.get("websockets")
previous_exceptions = sys.modules.get("websockets.exceptions")
sys.modules["websockets"] = websockets_stub
sys.modules["websockets.exceptions"] = exceptions_stub

module_name = "serve_realtime_ws_under_test"
sys.modules.pop(module_name, None)
spec = importlib.util.spec_from_file_location(module_name, SERVICE_PATH)
module = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(module)
try:
spec.loader.exec_module(module)
finally:
if previous_websockets is None:
sys.modules.pop("websockets", None)
else:
sys.modules["websockets"] = previous_websockets
if previous_exceptions is None:
sys.modules.pop("websockets.exceptions", None)
else:
sys.modules["websockets.exceptions"] = previous_exceptions
return module


def test_handler_catches_unexpected_errors_without_top_level_exceptions(
monkeypatch, caplog
):
module = load_service_module()

class FailingWebSocket:
remote_address = ("127.0.0.1", 12345)

def __aiter__(self):
return self

async def __anext__(self):
raise RuntimeError("boom")

class Session:
def __init__(self, *args, **kwargs):
pass

monkeypatch.setattr(
module, "load_models", lambda args: (object(), {}, object(), None)
)
monkeypatch.setattr(module, "DynamicStreamingVAD", lambda model: object())
monkeypatch.setattr(
module, "create_speaker_tracker", lambda model, args: None
)
monkeypatch.setattr(module, "RealtimeASRSession", Session)
args = types.SimpleNamespace(
device="cpu",
decode_interval=0.48,
partial_window_sec=15.0,
)

asyncio.run(module.handle_client(FailingWebSocket(), args))

assert "boom" in caplog.text


def test_cli_defaults_disable_speaker_and_bound_partial_window():
module = load_service_module()

Expand Down Expand Up @@ -1684,6 +1735,10 @@ def add_audio(self, message):
def should_decode(self):
return False

def decode(self, is_final=False):
assert is_final is True
return {"text": "", "sentences": []}

def session_stats(self):
return {
"duration_ms": self.total_samples,
Expand Down Expand Up @@ -1734,7 +1789,8 @@ async def send(self, message):
log_session_stats_interval=1.0,
)

asyncio.run(module.handle_client(FakeWebSocket(), args))
websocket = FakeWebSocket()
asyncio.run(module.handle_client(websocket, args))

assert logged == [
{
Expand All @@ -1748,3 +1804,4 @@ async def send(self, message):
"speaker_centers": 0,
}
]
assert websocket.sent[-1] == {"event": "stopped"}
Loading