docs: make multilingual README entrypoints task-oriented

Signed-off-by: zhifu gao <[email protected]>
This commit is contained in:
zhifu gao
2026-09-06 22:22:43 +00:00
parent 682ea10e80
commit 1aff355786
5 changed files with 338 additions and 147 deletions
+43 -39
View File
@@ -63,17 +63,16 @@ from funasr import AutoModel
model = AutoModel(model="FunAudioLLM/Fun-ASR-Nano-2512", device="cuda")
result = model.generate(input="https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/asr_example_zh.wav")
print(result[0]["text"])
# 欢迎大家来体验达摩院推出的语音识别模型。
```
For the separate 31-language checkpoint, use
[Fun-ASR-MLT-Nano-2512](https://huggingface.co/FunAudioLLM/Fun-ASR-MLT-Nano-2512).
Language coverage is checkpoint-specific, so Nano and MLT-Nano should be treated as distinct model choices.
On CPU (or for five-language ASR plus emotion and audio-event tags), use
**SenseVoiceSmall**. The pipeline below composes SenseVoiceSmall with FSMN-VAD
and CAM++; diarization is provided by the separate CAM++ model, not by the
SenseVoiceSmall checkpoint:
For a CPU-first example with five-language ASR plus emotion and audio-event
tags, use **SenseVoiceSmall**. The pipeline below combines it with FSMN-VAD and
CAM++ for speaker-aware VAD segments; these are not native speaker outputs of
the SenseVoiceSmall checkpoint.
See the [SenseVoice paper](https://arxiv.org/abs/2407.04051),
[Hugging Face checkpoint](https://huggingface.co/FunAudioLLM/SenseVoiceSmall),
and [GGUF edge checkpoint](https://huggingface.co/FunAudioLLM/SenseVoiceSmall-GGUF).
@@ -82,7 +81,7 @@ and [GGUF edge checkpoint](https://huggingface.co/FunAudioLLM/SenseVoiceSmall-GG
from funasr import AutoModel
from funasr.utils.postprocess_utils import rich_transcription_postprocess
model = AutoModel(model="iic/SenseVoiceSmall", vad_model="fsmn-vad", spk_model="cam++", device="cuda") # use device="cpu" if you don't have a GPU
model = AutoModel(model="iic/SenseVoiceSmall", vad_model="fsmn-vad", spk_model="cam++", device="cpu")
result = model.generate(
input="https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/asr_example_zh.wav",
batch_size_s=300,
@@ -93,13 +92,15 @@ for seg in result[0]["sentence_info"]:
print(f"[{seg['start']/1000:.1f}s] Speaker {seg['spk']}: {rich_transcription_postprocess(seg['sentence'])}")
```
**Output** — structured text with speaker labels, timestamps, and punctuation:
```
[0.6s] Speaker 0: 欢迎大家来体验达摩院推出的语音识别模型
```
This prints each returned segment's start time in seconds, anonymous speaker
index, and text with SenseVoice tags removed. Text and segment boundaries depend
on the audio and checkpoint; no fixed transcript is asserted here.
One `AutoModel` pipeline call coordinates the configured ASR, VAD, and speaker
models and returns the combined result.
CAM++ extracts `spk_embedding` vectors. `AutoModel` clusters those embeddings
and assigns speaker indices to VAD segments. Indices are local to a recording,
not known-person identities. See the [SDK contract](./docs/python_api.md) for
the component and result boundaries. Change to `device="cuda"` only after
verifying a compatible GPU environment as described above.
### Scale & deploy the flagship
@@ -120,23 +121,22 @@ results = model.generate(["audio1.wav", "audio2.wav"], language="auto")
### Why FunASR?
Whisper is a single model; **FunASR is a toolkit** — you pick the right model
per job: **Fun-ASR-Nano** (Chinese, English, Japanese, and Chinese dialects;
GPU), **Fun-ASR-MLT-Nano** (31 languages), **SenseVoiceSmall** (five-language
ASR plus emotion and audio events), and **Paraformer** (low-latency streaming).
The table shows toolkit-level capabilities and names the model or pipeline that
provides each one:
FunASR is a toolkit: choose the task, checkpoint, and runtime separately.
Support in one model or adapter does not imply support in every serving backend.
| | FunASR (toolkit) | Whisper | Cloud APIs |
| Task | Checkpoint or pipeline | Runtime entrypoint | Important limitation |
|---|---|---|---|
| Top speed | **340x realtime** (Fun-ASR-Nano + vLLM) | 13x realtime | ~1x realtime |
| Speaker ID | ✅ via VAD + CAM++ pipeline | ❌ Needs pyannote | ✅ Extra cost |
| Emotion | ✅ via SenseVoice | ❌ | ❌ |
| Languages | Checkpoint-specific (for example Qwen3-ASR 52, MLT-Nano 31, Nano zh/en/ja) | 57 | Varies |
| Streaming | ✅ WebSocket (Paraformer) | ❌ | ✅ |
| CPU viable | ✅ 17x realtime (SenseVoice) | ❌ Too slow | N/A |
| Self-hosted | ✅ Yes (toolkit: MIT; model licenses vary) | ✅ MIT license | ❌ Cloud only |
| Cost | Free | Free | $0.006/min+ |
| File transcription with emotion/event tags | SenseVoiceSmall | Python `AutoModel`, CPU or GPU | Five-language checkpoint; tags do not identify speakers. |
| LLM-based file transcription | Fun-ASR-Nano | `AutoModel`; split-engine `AutoModelVLLM` for the documented GPU path | Base Nano covers zh/en/ja and Chinese dialects/accents; timestamp support depends on checkpoint and path. |
| Broader multilingual transcription | Fun-ASR-MLT-Nano | Python `AutoModel` | Separate 31-language checkpoint; do not transfer its coverage to base Nano. |
| Chunked live transcription | Paraformer-zh-streaming | Streaming SDK or runtime WebSocket service | Use the streaming checkpoint and per-session cache, not an offline checkpoint. |
| Speaker-aware file transcription | SenseVoiceSmall + FSMN-VAD + CAM++ | `AutoModel` with VAD and embedding clustering | Anonymous indices within a recording, not enrolled-speaker identification. |
| Joint text, timestamps, and speakers | MOSS-Transcribe-Diarize, third-party OpenMOSS | FunASR adapter or upstream backend in the MOSS guide | Offline, recording-local anonymous labels; no external VAD/speaker pipeline for its unified path. |
| Native CPU/edge transcription | Fun-ASR-Nano or SenseVoiceSmall GGUF | llama.cpp runtime | Requires matching converted weights; GGUF is not a Python `AutoModel` checkpoint. |
See the [Model Zoo](./model_zoo/readme.md) and [deployment matrix](./docs/deployment_matrix.md)
for checkpoint, interface, and licensing boundaries. Benchmark on your own audio
and hardware before choosing a runtime.
Trying FunASR for the first time? Use the [Colab quickstart](./examples/colab/) before setting up a local environment. Choosing a first model? Start with the [model selection guide](./docs/model_selection.md). Planning a switch from Whisper or a cloud ASR provider? Use the [migration guide](./docs/migration_from_whisper.md) and [benchmark example](./examples/migration/) to test representative audio, map features, and roll out safely.
@@ -162,12 +162,17 @@ Requirements: Python ≥ 3.8. Install PyTorch + torchaudio first ([pytorch.org](
## Model Zoo
This list includes third-party models. **OpenMOSS** publishes MOSS-Transcribe-Diarize;
FunASR provides an adapter, not ownership of its weights. Its unified path is
offline, with anonymous labels scoped to each recording, not realtime or
known-person identification. Model licenses are separate from the toolkit's MIT license.
| Model | Task | Languages | Params | Links |
|-------|------|-----------|--------|-------|
| **Fun-ASR-Nano** | ASR | zh/en/ja + Chinese dialects and accents | 800M | [⭐](https://www.modelscope.cn/models/FunAudioLLM/Fun-ASR-Nano-2512) [🤗](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512) [GGUF](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-GGUF) |
| **Fun-ASR-MLT-Nano** | ASR | 31 languages | 800M | [⭐](https://www.modelscope.cn/models/FunAudioLLM/Fun-ASR-MLT-Nano-2512) [🤗](https://huggingface.co/FunAudioLLM/Fun-ASR-MLT-Nano-2512) |
| **SenseVoiceSmall** | ASR + emotion + events | zh/en/ja/ko/yue | 234M | [⭐](https://www.modelscope.cn/models/iic/SenseVoiceSmall) [🤗](https://huggingface.co/FunAudioLLM/SenseVoiceSmall) [GGUF](https://huggingface.co/FunAudioLLM/SenseVoiceSmall-GGUF) [paper](https://arxiv.org/abs/2407.04051) |
| **MOSS-Transcribe-Diarize** | Offline ASR + timestamps + anonymous speakers | See official card | See official card | [🤗](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize) [guide](./docs/moss_transcribe_diarize.md) |
| **MOSS-Transcribe-Diarize** | Third-party OpenMOSS: offline ASR + timestamps + anonymous speakers | See official card | See official card | [🤗](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize) [guide](./docs/moss_transcribe_diarize.md) |
| **Paraformer-zh** | ASR + timestamps | zh/en | 220M | [⭐](https://www.modelscope.cn/models/iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary) [🤗](https://huggingface.co/funasr/paraformer-zh) |
| Paraformer-zh-streaming | Streaming ASR | zh/en | 220M | [⭐](https://modelscope.cn/models/iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary) [🤗](https://huggingface.co/funasr/paraformer-zh-streaming) |
| Qwen3-ASR | ASR, 52 languages | multilingual | 1.7B | [usage](examples/industrial_data_pretraining/qwen3_asr) |
@@ -176,7 +181,7 @@ Requirements: Python ≥ 3.8. Install PyTorch + torchaudio first ([pytorch.org](
| Whisper-large-v3-turbo | ASR + translation | multilingual | 809M | [usage](examples/industrial_data_pretraining/whisper) |
| ct-punc | Punctuation | zh/en | 290M | [⭐](https://modelscope.cn/models/iic/punc_ct-transformer_cn-en-common-vocab471067-large/summary) [🤗](https://huggingface.co/funasr/ct-punc) |
| fsmn-vad | VAD | zh/en | 0.4M | [⭐](https://modelscope.cn/models/iic/speech_fsmn_vad_zh-cn-16k-common-pytorch/summary) [🤗](https://huggingface.co/funasr/fsmn-vad) |
| cam++ | Speaker diarization | — | 7.2M | [⭐](https://modelscope.cn/models/iic/speech_campplus_sv_zh-cn_16k-common/summary) [🤗](https://huggingface.co/funasr/campplus) |
| cam++ | Speaker embeddings (pipeline component) | — | 7.2M | [⭐](https://modelscope.cn/models/iic/speech_campplus_sv_zh-cn_16k-common/summary) [🤗](https://huggingface.co/funasr/campplus) |
| emotion2vec+large | Emotion recognition | — | 300M | [⭐](https://modelscope.cn/models/iic/emotion2vec_plus_large/summary) [🤗](https://huggingface.co/emotion2vec/emotion2vec_plus_large) |
---
@@ -324,17 +329,16 @@ inference on physical Blackwell hardware.
## Benchmark
> 184 long-form audio files (192 min). [Full report →](https://modelscope.github.io/FunASR/benchmark.html) · [RTFx and reproducibility notes →](./docs/benchmark/rtf_reproducibility.md)
The [historical benchmark report](https://modelscope.github.io/FunASR/benchmark.html)
and [split-engine measurements](./docs/vllm_guide.md#benchmark) retain their
original results. They are separate records, not universal speed rankings or
production capacity guarantees.
| Model | Chinese CER ↓ | GPU Speed | CPU Speed | vs Whisper-large-v3 |
|-------|------|-----------|-----------|-------------------|
| **Fun-ASR-Nano** (vLLM) | **8.20%** | **340x** realtime | — | 🚀 **26x faster** |
| **SenseVoice-Small** | **7.81%** | **170x** realtime | **17x** realtime | 🚀 **13x faster** |
| **Paraformer-Large** | 10.18% | **120x** realtime | **15x** realtime | 🚀 **9x faster** |
| Whisper-large-v3-turbo | 21.71% | 46x realtime | ❌ | 3.4x faster |
| Whisper-large-v3 | 20.02% | 13x realtime | ❌ | baseline |
> **Key takeaway:** FunASR models run on CPU faster than Whisper runs on GPU.
Use the [RTFx and reproducibility notes](./docs/benchmark/rtf_reproducibility.md)
to compare checkpoint/revision, audio set, hardware, batching, warmup, timing
scope, and CER/WER. Offline throughput is not streaming latency. The
[migration benchmark example](./examples/migration/) helps measure your own
recordings with the same evaluation scope.
---
+46 -37
View File
@@ -31,26 +31,34 @@
## クイックスタート
```bash
pip install funasr
python -m pip install torch torchaudio
python -m pip install funasr
```
以下は公開サンプルを使う CPU 向けの例です。GPU を使う場合は
[インストールガイド](./docs/installation/installation.md) に従って互換性のある
PyTorch/CUDA 環境を用意し、`torch.cuda.is_available()` を確認してから
`device="cuda"` に変更してください。
```python
from funasr import AutoModel
from funasr.utils.postprocess_utils import rich_transcription_postprocess
model = AutoModel(model="iic/SenseVoiceSmall", vad_model="fsmn-vad", spk_model="cam++", device="cuda")
result = model.generate(input="meeting.wav")
model = AutoModel(model="iic/SenseVoiceSmall", vad_model="fsmn-vad", spk_model="cam++", device="cpu")
result = model.generate(input="https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/asr_example_zh.wav")
for seg in result[0]["sentence_info"]:
print(f"[{seg['start']/1000:.1f}s] 話者{seg['spk']}: {rich_transcription_postprocess(seg['sentence'])}")
```
**出力** — 話者ラベル・タイムスタンプ・句読点付きの構造化テキスト:
```
[00:00.4 → 00:03.8] 話者0: Q3の計画について話し合いましょう。
[00:04.2 → 00:07.1] 話者1: いいですね。3つのポイントがあります。
[00:07.5 → 00:12.3] 話者0: どうぞ。あと30分あります。
```
実際に返された VAD 区間の開始時刻(秒)、匿名話者番号、SenseVoice タグを
除いたテキストを表示します。テキストや区間は音声と checkpoint に依存し、
ここでは固定の認識結果を示していません。
これは1回の `AutoModel` パイプライン呼び出しですが、SenseVoiceSmall、
FSMN-VAD、CAM++という独立したモデルを組み合わせています。話者分離は
SenseVoiceSmall自体ではなく、CAM++によって提供されます。
CAM++ は `spk_embedding` ベクトルを抽出し、`AutoModel` がクラスタリングと
VAD 区間への話者割り当てを行います。番号は録音内だけで有効で、既知の人物の
識別や SenseVoiceSmall 単体の出力ではありません。
詳細は [SDK 契約](./docs/python_api.md) を参照してください。
初めて使う場合は [Colab クイックスタート](./examples/colab/README_ja.md) から試せます。どのモデルを選ぶか迷う場合は [モデル選択ガイド](./docs/model_selection_ja.md) を参照してください。
@@ -60,22 +68,21 @@ SenseVoiceSmall自体ではなく、CAM++によって提供されます。
### なぜFunASRを選ぶのか?
Whisper は単一モデルですが、**FunASR はツールキット**です——用途に応じて
**Fun-ASR-Nano**(中・英・日と中国語方言・地域アクセント、GPU)、
**Fun-ASR-MLT-Nano**(31言語)、**SenseVoiceSmall**(5言語ASRと感情・
音声イベント)、**Paraformer**(低遅延ストリーミング)を選べます。
下の表はツールキット全体の機能と、それを提供するモデルまたはパイプラインを示します:
FunASR はツールキットです。タスク、checkpoint、ランタイムを別々に選びます。
あるモデルやアダプターの機能が、すべての配信バックエンドで使えるとは限りません。
| | FunASR(ツールキット) | Whisper | クラウドAPI |
| タスク | Checkpoint またはパイプライン | ランタイムの入口 | 主な制約 |
|---|---|---|---|
| 最高速度 | **340倍リアルタイム**(Fun-ASR-Nano + vLLM) | 13倍リアルタイム | 〜1倍リアルタイム |
| 話者認識 | ✅ VAD + CAM++パイプライン | ❌ pyannoteが必要 | ✅ 追加料金 |
| 感情認識 | ✅ SenseVoice による | ❌ | ❌ |
| 言語数 | チェックポイントごとに異なる(例:Qwen3-ASR 52、MLT-Nano 31、Nano 中/英/日) | 57 | サービスにより異なる |
| ストリーミング | ✅ WebSocket(Paraformer) | ❌ | ✅ |
| CPU対応 | ✅ 17倍リアルタイム(SenseVoice) | ❌ 遅すぎる | 該当なし |
| セルフホスト | ✅ 対応(ツールキット: MIT、モデルごとに異なる) | ✅ MITライセンス | ❌ クラウドのみ |
| コスト | 無料 | 無料 | $0.006/分〜 |
| ファイル認識と感情・イベントタグ | SenseVoiceSmall | Python `AutoModel`、CPU または GPU | 5言語の checkpoint。タグは話者の身元を示しません。 |
| LLM によるファイル認識 | Fun-ASR-Nano | `AutoModel`、または文書化された GPU 分離エンジン `AutoModelVLLM` | 基本 Nano は中・英・日と中国語方言・アクセント。timestamp は checkpoint と経路に依存します。 |
| より多くの言語のファイル認識 | Fun-ASR-MLT-Nano | Python `AutoModel` | 独立した31言語 checkpoint。その対応言語を基本 Nano に当てはめないでください。 |
| チャンク単位のライブ認識 | Paraformer-zh-streaming | ストリーミング SDK または runtime WebSocket | ストリーミング checkpoint とセッション別 cache が必要です。 |
| 話者付きファイル認識 | SenseVoiceSmall + FSMN-VAD + CAM++ | `AutoModel` の VAD と埋め込みクラスタリング | 番号は録音内の匿名ラベルで、登録済み人物の識別ではありません。 |
| テキスト・時刻・話者の同時生成 | 第三者 OpenMOSS の MOSS-Transcribe-Diarize | MOSS ガイドの FunASR adapter または上流バックエンド | オフライン、録音内の匿名ラベル。統合経路に外部 VAD/話者モデルは付けません。 |
| ネイティブ CPU/エッジ認識 | Fun-ASR-Nano または SenseVoiceSmall GGUF | llama.cpp runtime | 対応する変換済み重みが必要。GGUF は Python `AutoModel` 用 checkpoint ではありません。 |
[Model Zoo](./model_zoo/readme.md) と [デプロイ方式](./docs/deployment_matrix_ja.md)
でインターフェースとライセンスの制約を確認し、対象音声・ハードウェアで評価してください。
---
@@ -83,17 +90,14 @@ Whisper は単一モデルですが、**FunASR はツールキット**です—
## ベンチマーク
> 184件の長時間音声(計192分)。[詳細レポート →](https://modelscope.github.io/FunASR/benchmark.html)
[過去の評価レポート](https://modelscope.github.io/FunASR/benchmark.html) と
[分離エンジンの測定](./docs/vllm_guide.md#benchmark) に元の結果を残しています。
別々の記録であり、一般的な速度順位や本番容量を保証するものではありません。
| モデル | 中国語 CER ↓ | GPU速度 | CPU速度 | Whisper-large-v3比 |
|--------|------|---------|---------|-------------------|
| **Fun-ASR-Nano**(vLLM) | **8.20%** | **340倍**リアルタイム | — | 🚀 **26倍高速** |
| **SenseVoice-Small** | **7.81%** | **170倍**リアルタイム | **17倍**リアルタイム | 🚀 **13倍高速** |
| **Paraformer-Large** | 10.18% | **120倍**リアルタイム | **15倍**リアルタイム | 🚀 **9倍高速** |
| Whisper-large-v3-turbo | 21.71% | 46倍リアルタイム | ❌ | 3.4倍高速 |
| Whisper-large-v3 | 20.02% | 13倍リアルタイム | ❌ | ベースライン |
> **ポイント:** FunASRのCPU速度は、WhisperのGPU速度より速い。
[RTFx と再現性の説明](./docs/benchmark/rtf_reproducibility.md) に従い、
checkpoint/revision、音声セット、ハードウェア、バッチ、ウォームアップ、計測範囲、
CER/WER をそろえて比較してください。オフラインのスループットはストリーミングの
遅延ではありません。[移行評価の例](./examples/migration/) で自分の録音を評価できます。
---
@@ -121,12 +125,17 @@ pip install funasr
## モデル一覧
第三者モデルも含みます。MOSS-Transcribe-Diarize の公開元は **OpenMOSS** で、
FunASR はアダプターを提供します。統合経路はオフラインで、匿名話者ラベルは
録音内だけで有効です。リアルタイム処理や既知の人物の識別ではありません。
モデルのライセンスはツールキットの MIT ライセンスとは別に確認してください。
| モデル | タスク | 言語 | パラメータ | リンク |
|--------|--------|------|-----------|--------|
| **Fun-ASR-Nano** | 認識 | 中/英/日 + 中国語方言 | 800M | [⭐](https://www.modelscope.cn/models/FunAudioLLM/Fun-ASR-Nano-2512) [🤗](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512) [GGUF](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-GGUF) |
| **Fun-ASR-MLT-Nano** | 認識 | 31言語 | 800M | [⭐](https://www.modelscope.cn/models/FunAudioLLM/Fun-ASR-MLT-Nano-2512) [🤗](https://huggingface.co/FunAudioLLM/Fun-ASR-MLT-Nano-2512) |
| **SenseVoiceSmall** | 認識 + 感情 + イベント | 中/英/日/韓/粤 | 234M | [⭐](https://www.modelscope.cn/models/iic/SenseVoiceSmall) [🤗](https://huggingface.co/FunAudioLLM/SenseVoiceSmall) [GGUF](https://huggingface.co/FunAudioLLM/SenseVoiceSmall-GGUF) |
| **MOSS-Transcribe-Diarize** | オフライン認識 + タイムスタンプ + 匿名話者 | 公式モデルカードを参照 | 公式モデルカードを参照 | [🤗](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize) [ガイド](./docs/moss_transcribe_diarize.md) |
| **MOSS-Transcribe-Diarize** | 第三者 OpenMOSS:オフライン認識 + タイムスタンプ + 匿名話者 | 公式モデルカードを参照 | 公式モデルカードを参照 | [🤗](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize) [ガイド](./docs/moss_transcribe_diarize.md) |
| **Paraformer-zh** | 認識 + タイムスタンプ | 中/英 | 220M | [⭐](https://www.modelscope.cn/models/iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary) [🤗](https://huggingface.co/funasr/paraformer-zh) |
| Qwen3-ASR | 認識、52言語 | 多言語 | 1.7B | [使用法](examples/industrial_data_pretraining/qwen3_asr) |
| GLM-ASR-Nano | 認識、17言語 | 多言語 | 1.5B | [使用法](examples/industrial_data_pretraining/glm_asr) |
+45 -37
View File
@@ -31,26 +31,33 @@
## 빠른 시작
```bash
pip install funasr
python -m pip install torch torchaudio
python -m pip install funasr
```
아래는 공개 샘플을 사용하는 CPU 우선 예제입니다. GPU를 사용하려면
[설치 가이드](./docs/installation/installation.md)에 따라 호환되는 PyTorch/CUDA
환경을 준비하고 `torch.cuda.is_available()`을 확인한 뒤 `device="cuda"`로 바꾸세요.
```python
from funasr import AutoModel
from funasr.utils.postprocess_utils import rich_transcription_postprocess
model = AutoModel(model="iic/SenseVoiceSmall", vad_model="fsmn-vad", spk_model="cam++", device="cuda")
result = model.generate(input="meeting.wav")
model = AutoModel(model="iic/SenseVoiceSmall", vad_model="fsmn-vad", spk_model="cam++", device="cpu")
result = model.generate(input="https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/asr_example_zh.wav")
for seg in result[0]["sentence_info"]:
print(f"[{seg['start']/1000:.1f}s] 화자{seg['spk']}: {rich_transcription_postprocess(seg['sentence'])}")
```
**출력** — 화자 라벨, 타임스탬프, 구두점이 포함된 구조화된 텍스트:
```
[00:00.4 → 00:03.8] 화자0: Q3 계획에 대해 논의하겠습니다.
[00:04.2 → 00:07.1] 화자1: 좋습니다. 세 가지 포인트가 있습니다.
[00:07.5 → 00:12.3] 화자0: 말씀하세요. 30분 남았습니다.
```
실제로 반환된 VAD 구간의 시작 시각(초), 익명 화자 번호, SenseVoice 태그를 제거한
텍스트를 출력합니다. 텍스트와 구간은 오디오와 checkpoint에 따라 달라지므로
고정된 인식 결과를 제시하지 않습니다.
한 번의 `AutoModel` 파이프라인 호출이지만, SenseVoiceSmall, FSMN-VAD,
CAM++라는 독립된 모델을 조합합니다. 화자 분리는 SenseVoiceSmall 자체가 아니라
CAM++에서 제공합니다.
CAM++는 `spk_embedding` 벡터를 추출하고, `AutoModel`이 클러스터링하여 VAD
구간에 화자 번호를 할당합니다. 번호는 해당 녹음 안에서만 유효하며 알려진 인물의
신원이나 SenseVoiceSmall 단독 출력이 아닙니다. 자세한 내용은
[SDK 계약](./docs/python_api.md)을 참고하세요.
처음 사용한다면 [Colab 빠른 시작](./examples/colab/README_ko.md)으로 먼저 확인할 수 있습니다. 어떤 모델을 선택할지 고민된다면 [모델 선택 가이드](./docs/model_selection_ko.md)를 참고하세요.
@@ -60,22 +67,21 @@ CAM++에서 제공합니다.
### 왜 FunASR인가?
Whisper는 단일 모델이지만, **FunASR는 툴킷**입니다. 용도에 맞게
**Fun-ASR-Nano**(중국어/영어/일본어 및 중국어 방언/지역 억양, GPU),
**Fun-ASR-MLT-Nano**(31개 언어), **SenseVoiceSmall**(5개 언어 ASR와
감정·오디오 이벤트), **Paraformer**(저지연 스트리밍)를 선택하세요.
아래 표는 툴킷 전체의 기능과 이를 제공하는 모델 또는 파이프라인을 보여 줍니다:
FunASR는 툴킷입니다. 작업, checkpoint, 런타임을 각각 선택해야 합니다.
한 모델이나 어댑터가 지원하는 기능이 모든 서빙 백엔드에서 지원되는 것은 아닙니다.
| | FunASR(툴킷) | Whisper | 클라우드 API |
| 작업 | Checkpoint 또는 파이프라인 | 런타임 진입점 | 주요 제한 |
|---|---|---|---|
| 최고 속도 | **340배 실시간**(Fun-ASR-Nano + vLLM) | 13배 실시간 | ~1배 실시간 |
| 화자 인식 | ✅ VAD + CAM++ 파이프라인 | ❌ pyannote 필요 | ✅ 추가 비용 |
| 감정 인식 | ✅ SenseVoice 제공 | ❌ | ❌ |
| 언어 수 | 체크포인트별 상이(예: Qwen3-ASR 52, MLT-Nano 31, Nano 중/영/일) | 57개 | 서비스마다 다름 |
| 스트리밍 | ✅ WebSocket(Paraformer) | ❌ | ✅ |
| CPU 사용 | ✅ 17배 실시간(SenseVoice) | ❌ 너무 느림 | 해당 없음 |
| 자체 호스팅 | ✅ 지원 (툴킷: MIT, 모델별 상이) | ✅ MIT 라이선스 | ❌ 클라우드만 |
| 비용 | 무료 | 무료 | $0.006/분~ |
| 파일 전사와 감정/이벤트 태그 | SenseVoiceSmall | Python `AutoModel`, CPU 또는 GPU | 5개 언어 checkpoint이며 태그는 화자 신원을 나타내지 않습니다. |
| LLM 기반 파일 전사 | Fun-ASR-Nano | `AutoModel`, 또는 문서의 GPU 분리 엔진 `AutoModelVLLM` | 기본 Nano는 중/영/일 및 중국어 방언/억양을 지원하며 timestamp는 checkpoint와 경로에 따라 다릅니다. |
| 더 많은 언어의 파일 전사 | Fun-ASR-MLT-Nano | Python `AutoModel` | 별도의 31개 언어 checkpoint이며 기본 Nano에 같은 범위를 적용하지 않습니다. |
| 청크 단위 실시간 전사 | Paraformer-zh-streaming | 스트리밍 SDK 또는 runtime WebSocket | 스트리밍 checkpoint와 세션별 cache가 필요하며 오프라인 checkpoint로 대체할 수 없습니다. |
| 화자별 파일 전사 | SenseVoiceSmall + FSMN-VAD + CAM++ | `AutoModel`의 VAD와 임베딩 클러스터링 | 녹음 안의 익명 번호이며 등록된 인물의 신원 식별이 아닙니다. |
| 텍스트, 시각, 화자 공동 생성 | 제3자 OpenMOSS의 MOSS-Transcribe-Diarize | MOSS 가이드의 FunASR adapter 또는 업스트림 백엔드 | 오프라인, 녹음 내 익명 라벨이며 통합 경로에 외부 VAD/화자 모델을 붙이지 않습니다. |
| 네이티브 CPU/엣지 전사 | Fun-ASR-Nano 또는 SenseVoiceSmall GGUF | llama.cpp runtime | 호환되는 변환 가중치가 필요하며 GGUF는 Python `AutoModel`용 checkpoint가 아닙니다. |
[Model Zoo](./model_zoo/readme.md)와 [배포 매트릭스](./docs/deployment_matrix_ko.md)에서
인터페이스와 라이선스 제한을 확인하고 대상 오디오와 하드웨어로 평가하세요.
---
@@ -83,17 +89,14 @@ Whisper는 단일 모델이지만, **FunASR는 툴킷**입니다. 용도에 맞
## 벤치마크
> 184개 장시간 오디오(총 192분). [상세 보고서 →](https://modelscope.github.io/FunASR/benchmark.html)
[기존 평가 보고서](https://modelscope.github.io/FunASR/benchmark.html)와
[분리 엔진 측정](./docs/vllm_guide.md#benchmark)에 원래 결과를 보존합니다.
서로 다른 기록이며 보편적인 속도 순위나 운영 용량을 보장하지 않습니다.
| 모델 | 중국어 CER ↓ | GPU 속도 | CPU 속도 | Whisper-large-v3 대비 |
|------|------|----------|----------|---------------------|
| **Fun-ASR-Nano**(vLLM) | **8.20%** | **340배** 실시간 | — | 🚀 **26배 빠름** |
| **SenseVoice-Small** | **7.81%** | **170배** 실시간 | **17배** 실시간 | 🚀 **13배 빠름** |
| **Paraformer-Large** | 10.18% | **120배** 실시간 | **15배** 실시간 | 🚀 **9배 빠름** |
| Whisper-large-v3-turbo | 21.71% | 46배 실시간 | ❌ | 3.4배 빠름 |
| Whisper-large-v3 | 20.02% | 13배 실시간 | ❌ | 기준선 |
> **핵심:** FunASR의 CPU 속도가 Whisper의 GPU 속도보다 빠릅니다.
[RTFx와 재현성 설명](./docs/benchmark/rtf_reproducibility.md)에 따라
checkpoint/revision, 오디오 집합, 하드웨어, 배치, 워밍업, 측정 범위, CER/WER를
맞춰 비교하세요. 오프라인 처리량은 스트리밍 지연이 아닙니다.
[마이그레이션 평가 예제](./examples/migration/)로 자신의 녹음을 측정할 수 있습니다.
---
@@ -121,12 +124,17 @@ pip install funasr
## 모델 목록
제3자 모델도 포함합니다. MOSS-Transcribe-Diarize의 배포 주체는 **OpenMOSS**이며
FunASR는 어댑터를 제공합니다. 통합 경로는 오프라인이고 익명 화자 라벨은 해당 녹음
안에서만 유효합니다. 실시간 처리나 알려진 인물의 신원 식별이 아닙니다.
모델 라이선스는 툴킷의 MIT 라이선스와 별도로 확인해야 합니다.
| 모델 | 작업 | 언어 | 파라미터 | 링크 |
|------|------|------|---------|------|
| **Fun-ASR-Nano** | 인식 | 중/영/일 + 중국어 방언 | 800M | [⭐](https://www.modelscope.cn/models/FunAudioLLM/Fun-ASR-Nano-2512) [🤗](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512) [GGUF](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-GGUF) |
| **Fun-ASR-MLT-Nano** | 인식 | 31개 언어 | 800M | [⭐](https://www.modelscope.cn/models/FunAudioLLM/Fun-ASR-MLT-Nano-2512) [🤗](https://huggingface.co/FunAudioLLM/Fun-ASR-MLT-Nano-2512) |
| **SenseVoiceSmall** | 인식 + 감정 + 이벤트 | 중/영/일/한/광둥어 | 234M | [⭐](https://www.modelscope.cn/models/iic/SenseVoiceSmall) [🤗](https://huggingface.co/FunAudioLLM/SenseVoiceSmall) [GGUF](https://huggingface.co/FunAudioLLM/SenseVoiceSmall-GGUF) |
| **MOSS-Transcribe-Diarize** | 오프라인 인식 + 타임스탬프 + 익명 화자 | 공식 모델 카드 참조 | 공식 모델 카드 참조 | [🤗](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize) [가이드](./docs/moss_transcribe_diarize.md) |
| **MOSS-Transcribe-Diarize** | 제3자 OpenMOSS: 오프라인 인식 + 타임스탬프 + 익명 화자 | 공식 모델 카드 참조 | 공식 모델 카드 참조 | [🤗](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize) [가이드](./docs/moss_transcribe_diarize.md) |
| **Paraformer-zh** | 인식 + 타임스탬프 | 중/영 | 220M | [⭐](https://www.modelscope.cn/models/iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary) [🤗](https://huggingface.co/funasr/paraformer-zh) |
| Qwen3-ASR | 인식, 52개 언어 | 다국어 | 1.7B | [사용법](examples/industrial_data_pretraining/qwen3_asr) |
| GLM-ASR-Nano | 인식, 17개 언어 | 다국어 | 1.5B | [사용법](examples/industrial_data_pretraining/glm_asr) |
+35 -34
View File
@@ -53,11 +53,13 @@ PY
只有这里输出 `True` 时才使用 `device="cuda"`;否则请先使用
`device="cpu"`,或重新安装匹配 CUDA 的 PyTorch wheel。
以下 SenseVoiceSmall 示例先使用 CPU,并组合 FSMN-VAD 与 CAM++。
```python
from funasr import AutoModel
from funasr.utils.postprocess_utils import rich_transcription_postprocess
model = AutoModel(model="iic/SenseVoiceSmall", vad_model="fsmn-vad", spk_model="cam++", device="cuda")
model = AutoModel(model="iic/SenseVoiceSmall", vad_model="fsmn-vad", spk_model="cam++", device="cpu")
result = model.generate(input="https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/asr_example_zh.wav")
# AutoModel pipeline 返回带说话人 id 和时间戳的 VAD 分段:
@@ -65,14 +67,13 @@ for seg in result[0]["sentence_info"]:
print(f"[{seg['start']/1000:.1f}s] 说话人{seg['spk']}: {rich_transcription_postprocess(seg['sentence'])}")
```
**输出** — 带说话人标签、时间戳和标点的结构化文本:
```
[0.6s] 说话人0: 欢迎大家来体验达摩院推出的语音识别模型
```
代码打印实际返回的 VAD 分段起点(秒)、匿名说话人编号和去除 SenseVoice
标签后的文本。文本与分段边界取决于音频和 checkpoint,这里不预设转写结果。
这是一次 `AutoModel` pipeline 调用,实际组合了 SenseVoiceSmall、FSMN-VAD
和 CAM++ 三个独立模型;说话人分离由 CAM++ 提供,并非 SenseVoiceSmall
checkpoint 的内置输出。
CAM++ 提取 `spk_embedding` 说话人嵌入,`AutoModel` 再聚类并为 VAD 分段分配
说话人编号;编号仅在当前录音内有效,不是已知人物身份,也不是 SenseVoiceSmall
checkpoint 的内置输出。组件和返回字段见 [SDK 契约](./docs/python_api_zh.md)。
需要 GPU 时,先按上文验证环境,再将 `device` 改为 `"cuda"`。
SenseVoice 论文见 [arXiv:2407.04051](https://arxiv.org/abs/2407.04051),
模型见 [Hugging Face checkpoint](https://huggingface.co/FunAudioLLM/SenseVoiceSmall),
边缘部署可用 [GGUF checkpoint](https://huggingface.co/FunAudioLLM/SenseVoiceSmall-GGUF)。
@@ -110,22 +111,21 @@ results = model.generate(["audio1.wav", "audio2.wav"], language="auto")
### 为什么选 FunASR?
Whisper 是单个模型,**FunASR 是一个工具箱**——按场景挑模型:
**Fun-ASR-Nano**(中/英/日及中文方言,需 GPU)、
**Fun-ASR-MLT-Nano**(31 语种)、**SenseVoiceSmall**(五语种 ASR,
并返回情感与音频事件标签)、**Paraformer**(低延迟流式)。下表展示的是
工具箱级能力,并标明由哪个模型或 pipeline 提供:
FunASR 是工具箱,需要分别选择任务、checkpoint 和运行时。
一个模型或适配器支持的能力,不代表所有服务后端都支持。
| | FunASR(工具箱) | Whisper | 云端 API |
| 任务 | Checkpoint 或 pipeline | 运行时入口 | 主要边界 |
|---|---|---|---|
| 最高速度 | **340 倍实时**(Fun-ASR-Nano + vLLM) | 13 倍实时 | ~1 倍实时 |
| 说话人识别 | ✅ 由 VAD + CAM++ pipeline 提供 | ❌ 需要 pyannote | ✅ 额外付费 |
| 情感识别 | ✅ 由 SenseVoice 提供 | ❌ | ❌ |
| 语言数 | 取决于 checkpoint(例如 Qwen3-ASR 52、MLT-Nano 31、Nano 中/英/日) | 57 | 因服务而异 |
| 流式识别 | ✅ WebSocket(Paraformer) | ❌ | ✅ |
| CPU 可用 | ✅ 17 倍实时(SenseVoice) | ❌ 太慢 | 不适用 |
| 私有部署 | ✅ 支持(工具箱 MIT;模型协议各异) | ✅ MIT 开源 | ❌ 仅云端 |
| 费用 | 免费 | 免费 | ¥0.04/分钟起 |
| 文件转写及情感/事件标签 | SenseVoiceSmall | Python `AutoModel`,CPU 或 GPU | 五语种 checkpoint;标签不代表说话人身份。 |
| LLM 文件转写 | Fun-ASR-Nano | `AutoModel`;文档指定的 GPU 拆分引擎 `AutoModelVLLM` | 基础 Nano 覆盖中/英/日及中文方言/口音;时间戳取决于 checkpoint 和路径。 |
| 更多语种的文件转写 | Fun-ASR-MLT-Nano | Python `AutoModel` | 独立的 31 语种 checkpoint,不能把其覆盖范围归给基础 Nano。 |
| 分块实时转写 | Paraformer-zh-streaming | 流式 SDK 或 runtime WebSocket 服务 | 使用流式 checkpoint 和会话独立 cache,不能替换为离线 checkpoint。 |
| 带说话人的文件转写 | SenseVoiceSmall + FSMN-VAD + CAM++ | `AutoModel` 的 VAD 与嵌入聚类 | 编号仅在当前录音内有效,不是已注册人物身份识别。 |
| 联合文本、时间戳与说话人 | 第三方 OpenMOSS 的 MOSS-Transcribe-Diarize | MOSS 指南中的 FunASR adapter 或上游后端 | 离线、录音内匿名标签;统一路径不外接 VAD/说话人 pipeline。 |
| 原生 CPU/端侧转写 | Fun-ASR-Nano 或 SenseVoiceSmall GGUF | llama.cpp runtime | 需要匹配的转换权重,GGUF 不能作为 Python `AutoModel` checkpoint 加载。 |
Checkpoint、接口与协议边界见 [Model Zoo](./model_zoo/readme_zh.md) 和
[部署矩阵](./docs/deployment_matrix_zh.md)。请用目标音频与硬件评测后再选运行时。
第一次试用 FunASR?可以先跑 [Colab 快速体验](./examples/colab/README_zh.md),再配置本地环境。还不确定先用哪个模型?先看 [模型选择指南](./docs/model_selection_zh.md)。计划从 Whisper 或云端 ASR 切换?请按 [迁移指南](./docs/migration_from_whisper_zh.md) 和 [评测示例](./examples/migration/) 用代表性音频评测、映射功能并安全上线。
@@ -135,17 +135,14 @@ Whisper 是单个模型,**FunASR 是一个工具箱**——按场景挑模型
## 性能评测
> 184 条长音频(共 192 分钟)。[完整报告 →](https://modelscope.github.io/FunASR/zh/benchmark.html)
[历史评测报告](https://modelscope.github.io/FunASR/zh/benchmark.html) 与
[拆分引擎测量](./docs/vllm_guide_zh.md#benchmark) 保留原始结果。
两者是独立记录,不能合并成通用速度排名或生产容量承诺。
| 模型 | 中文 CER ↓ | GPU 速度 | CPU 速度 | 对比 Whisper-large-v3 |
|------|------|----------|----------|---------------------|
| **Fun-ASR-Nano**(vLLM) | **8.20%** | **340 倍**实时 | — | 🚀 **快 26 倍** |
| **SenseVoice-Small** | **7.81%** | **170 倍**实时 | **17 倍**实时 | 🚀 **快 13 倍** |
| **Paraformer-Large** | 10.18% | **120 倍**实时 | **15 倍**实时 | 🚀 **快 9 倍** |
| Whisper-large-v3-turbo | 21.71% | 46 倍实时 | ❌ | 快 3.4 倍 |
| Whisper-large-v3 | 20.02% | 13 倍实时 | ❌ | 基准 |
> **一句话:** FunASR 在 CPU 上的速度,比 Whisper 在 GPU 上还快。
请按 [RTFx 与可复现评测说明](./docs/benchmark/rtf_reproducibility.md) 对齐
checkpoint/revision、音频集、硬件、批量大小、预热、计时范围与 CER/WER。
离线吞吐量不等于流式延迟;可使用 [迁移评测示例](./examples/migration/)
以相同口径测量自己的录音。
---
@@ -181,12 +178,16 @@ pip install -e ./
## 模型列表
列表包含第三方模型。MOSS-Transcribe-Diarize 由 **OpenMOSS** 发布,FunASR
提供适配器,并不拥有其权重。统一路径为离线处理,匿名说话人标签仅在当前录音
内有效,不是实时流式或已知人物身份识别。模型协议与工具箱 MIT 协议分别适用。
| 模型 | 任务 | 语言 | 参数量 | 链接 |
|------|------|------|--------|------|
| **Fun-ASR-Nano** | 识别 | 中/英/日 + 中文方言 | 800M | [⭐](https://www.modelscope.cn/models/FunAudioLLM/Fun-ASR-Nano-2512) [🤗](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512) [GGUF](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-GGUF) |
| **Fun-ASR-MLT-Nano** | 识别 | 31 种语言 | 800M | [⭐](https://www.modelscope.cn/models/FunAudioLLM/Fun-ASR-MLT-Nano-2512) [🤗](https://huggingface.co/FunAudioLLM/Fun-ASR-MLT-Nano-2512) |
| **SenseVoiceSmall** | 识别 + 情感 + 事件 | 中/英/日/韩/粤 | 234M | [⭐](https://www.modelscope.cn/models/iic/SenseVoiceSmall) [🤗](https://huggingface.co/FunAudioLLM/SenseVoiceSmall) [GGUF](https://huggingface.co/FunAudioLLM/SenseVoiceSmall-GGUF) [论文](https://arxiv.org/abs/2407.04051) |
| **MOSS-Transcribe-Diarize** | 离线识别 + 时间戳 + 匿名说话人 | 以官方模型卡为准 | 以官方模型卡为准 | [🤗](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize) [指南](./docs/moss_transcribe_diarize_zh.md) |
| **MOSS-Transcribe-Diarize** | 第三方 OpenMOSS:离线识别 + 时间戳 + 匿名说话人 | 以官方模型卡为准 | 以官方模型卡为准 | [🤗](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize) [指南](./docs/moss_transcribe_diarize_zh.md) |
| **Paraformer-zh** | 识别 + 时间戳 | 中/英 | 220M | [⭐](https://www.modelscope.cn/models/iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary) [🤗](https://huggingface.co/funasr/paraformer-zh) |
| Paraformer-zh-streaming | 流式识别 | 中/英 | 220M | [⭐](https://modelscope.cn/models/iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary) [🤗](https://huggingface.co/funasr/paraformer-zh-streaming) |
| Qwen3-ASR | 识别,52 种语言 | 多语言 | 1.7B | [使用](examples/industrial_data_pretraining/qwen3_asr) |
@@ -195,7 +196,7 @@ pip install -e ./
| Whisper-large-v3-turbo | 识别 + 翻译 | 多语言 | 809M | [使用](examples/industrial_data_pretraining/whisper) |
| ct-punc | 标点恢复 | 中/英 | 290M | [⭐](https://modelscope.cn/models/iic/punc_ct-transformer_cn-en-common-vocab471067-large/summary) [🤗](https://huggingface.co/funasr/ct-punc) |
| fsmn-vad | 语音检测 | 中/英 | 0.4M | [⭐](https://modelscope.cn/models/iic/speech_fsmn_vad_zh-cn-16k-common-pytorch/summary) [🤗](https://huggingface.co/funasr/fsmn-vad) |
| cam++ | 说话人分离 | — | 7.2M | [⭐](https://modelscope.cn/models/iic/speech_campplus_sv_zh-cn_16k-common/summary) [🤗](https://huggingface.co/funasr/campplus) |
| cam++ | 说话人嵌入(speaker embeddings,pipeline 组件) | — | 7.2M | [⭐](https://modelscope.cn/models/iic/speech_campplus_sv_zh-cn_16k-common/summary) [🤗](https://huggingface.co/funasr/campplus) |
| emotion2vec+large | 情感识别 | — | 300M | [⭐](https://modelscope.cn/models/iic/emotion2vec_plus_large/summary) [🤗](https://huggingface.co/emotion2vec/emotion2vec_plus_large) |
---
+169
View File
@@ -0,0 +1,169 @@
"""README entrypoint contracts; parse examples without importing or running models."""
import ast
from pathlib import Path
import re
import subprocess
from urllib.parse import unquote, urlsplit
from bs4 import BeautifulSoup
from markdown import Markdown
import pytest
ROOT = Path(__file__).resolve().parents[1]
LANGUAGES = {
"README.md": ("Why FunASR?", "Benchmark", "What's new", "Model Zoo", "offline", "anonymous", "recording"),
"README_zh.md": ("为什么选 FunASR?", "性能评测", "最新动态", "模型列表", "离线", "匿名", "录音"),
"README_ja.md": ("なぜFunASRを選ぶのか?", "ベンチマーク", "最新情報", "モデル一覧", "オフライン", "匿名", "録音"),
"README_ko.md": ("왜 FunASR인가?", "벤치마크", "최신 소식", "모델 목록", "오프라인", "익명", "녹음"),
}
def read(name):
return (ROOT / name).read_text(encoding="utf-8")
def fences(text):
language = None
lines = []
for line in text.splitlines():
if line.startswith("```"):
if language is None:
language, lines = line[3:].strip(), []
else:
yield language, "\n".join(lines) + "\n"
language = None
elif language is not None:
lines.append(line)
assert language is None, "Unclosed code fence"
def render(text):
def slugify(value, separator):
return re.sub(r"[^\w\- ]", "", value.lower()).replace(" ", separator)
return BeautifulSoup(Markdown(
extensions=["extra", "toc"],
extension_configs={"toc": {"slugify": slugify}},
).convert(text), "html.parser")
def section(name, title):
soup = render(read(name))
heading = soup.find(re.compile(r"^h[1-6]$"), string=title)
assert heading is not None, title
nodes = []
for node in heading.next_siblings:
if node.name and re.fullmatch(r"h[1-6]", node.name) and node.name <= heading.name:
break
nodes.append(str(node))
return BeautifulSoup("".join(nodes), "html.parser")
@pytest.mark.parametrize("name", LANGUAGES)
def test_task_table_names_checkpoint_runtime_and_boundaries(name):
body = section(name, LANGUAGES[name][0])
table = body.find("table")
assert table is not None
assert len(table.select("thead th")) == 4
assert not any(word in table.get_text() for word in ("Whisper", "Cloud APIs", "$0.006", "¥0.04"))
for checkpoint in ("SenseVoiceSmall", "Fun-ASR-Nano", "Fun-ASR-MLT-Nano", "Paraformer", "MOSS-Transcribe-Diarize", "llama.cpp"):
assert checkpoint in table.get_text(), checkpoint
assert "OpenMOSS" in table.get_text()
assert "CPU" in table.get_text() and "WebSocket" in table.get_text()
assert all(len(row.find_all("td")) == 4 for row in table.select("tbody tr"))
@pytest.mark.parametrize("name", LANGUAGES)
def test_benchmark_links_evidence_instead_of_generalizing_vendor_performance(name):
body = section(name, LANGUAGES[name][1])
assert not body.find("table"), "Keep historical numeric tables in their evidence reports"
links = [a.get("href", "") for a in body.find_all("a")]
assert any("docs/benchmark/rtf_reproducibility.md" in href for href in links)
assert any("benchmark.html" in href for href in links)
assert "Whisper" not in body.get_text(), "Do not promote a cross-hardware comparison into a blanket claim"
assert not re.search(r"[$¥]\s*0\.\d+", read(name))
@pytest.mark.parametrize("name", LANGUAGES)
def test_first_sensevoice_example_is_cpu_and_prints_real_return_fields(name):
examples = [code for language, code in fences(read(name))
if language == "python" and "SenseVoiceSmall" in code]
assert examples
tree = ast.parse(examples[0])
calls = [node for node in ast.walk(tree) if isinstance(node, ast.Call)]
constructor = next(node for node in calls if isinstance(node.func, ast.Name) and node.func.id == "AutoModel")
options = {kw.arg: ast.literal_eval(kw.value) for kw in constructor.keywords}
assert options["device"] == "cpu"
assert options["vad_model"] == "fsmn-vad" and options["spk_model"] == "cam++"
generate = next(node for node in calls if isinstance(node.func, ast.Attribute) and node.func.attr == "generate")
sample = next(ast.literal_eval(kw.value) for kw in generate.keywords if kw.arg == "input")
assert sample.startswith("https://") and "asr_example_zh.wav" in sample
assert "asr_example_zh.wav" in read("examples/industrial_data_pretraining/paraformer/demo.py")
assert any(isinstance(node.func, ast.Name) and node.func.id == "print" for node in calls)
fields = {node.slice.value for node in ast.walk(tree)
if isinstance(node, ast.Subscript) and isinstance(node.slice, ast.Constant)}
assert {"sentence_info", "start", "spk", "sentence"} <= fields
assert "spk_embedding" in read(name), "Explain embeddings separately from pipeline diarization"
@pytest.mark.parametrize("name", LANGUAGES)
def test_model_zoo_retains_third_party_moss_and_recording_scope(name):
body = section(name, LANGUAGES[name][3])
moss = next(row for row in body.select("tr") if "MOSS-Transcribe-Diarize" in row.get_text())
assert "OpenMOSS" in moss.get_text()
for boundary in LANGUAGES[name][4:]:
assert boundary in body.get_text().lower(), boundary
assert any("docs/moss_transcribe_diarize" in a.get("href", "") for a in moss.find_all("a"))
for row in body.select("tr"):
if row.find("td") and row.find("td").get_text(strip=True) == "cam++":
assert "embedding" in row.find_all("td")[1].get_text().lower()
@pytest.mark.parametrize("name", LANGUAGES)
def test_news_stays_three_items_and_keeps_release_history(name):
body = section(name, LANGUAGES[name][2])
assert len(body.select("li")) == 3
assert any(a.get("href") == "https://github.com/modelscope/FunASR/releases" for a in body.find_all("a"))
@pytest.mark.parametrize("name", LANGUAGES)
def test_python_and_shell_examples_have_valid_syntax_without_execution(name):
for language, code in fences(read(name)):
if language == "python":
compile(code, name, "exec")
elif language in {"bash", "sh", "shell"}:
result = subprocess.run(["bash", "-n"], input=code, text=True, capture_output=True)
assert result.returncode == 0, result.stderr
for embedded in re.findall(r"<<'PY'\n(.*?)\nPY", code, re.S):
ast.parse(embedded)
@pytest.mark.parametrize("name", LANGUAGES)
def test_relative_links_and_internal_navigation_resolve(name):
soup = render(read(name))
for link in soup.select("a[href]"):
target = urlsplit(link["href"])
if target.scheme or target.netloc:
continue
resolved = (ROOT / unquote(target.path)).resolve() if target.path else ROOT / name
assert resolved.is_relative_to(ROOT)
assert resolved.exists(), (name, link["href"])
if target.fragment and resolved.suffix == ".md":
destination = render(resolved.read_text(encoding="utf-8"))
anchors = {node.get("id") for node in destination.select("[id]")}
anchors.update(node.get("name") for node in destination.select("a[name]"))
assert unquote(target.fragment) in anchors, (name, link["href"])
def test_speaker_field_explanation_has_implementation_evidence():
camp = ast.parse(read("funasr/models/campplus/model.py"))
assert any(isinstance(node, ast.Constant) and node.value == "spk_embedding" for node in ast.walk(camp))
auto = ast.parse(read("funasr/auto/auto_model.py"))
strings = {node.value for node in ast.walk(auto) if isinstance(node, ast.Constant) and isinstance(node.value, str)}
assert {"sentence_info", "start", "sentence"} <= strings
assert any(isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == "distribute_spk" for node in ast.walk(auto))
utility = ast.parse(read("funasr/models/campplus/utils.py"))
distribute = next(node for node in utility.body if isinstance(node, ast.FunctionDef) and node.name == "distribute_spk")
assert any(isinstance(node, ast.Constant) and node.value == "spk" for node in ast.walk(distribute))