mirror of
https://github.com/modelscope/FunASR.git
synced 2026-10-12 02:19:18 +00:00
docs: redesign product site and unify documentation entry points
Signed-off-by: zhifu gao <[email protected]>
This commit is contained in:
@@ -3,6 +3,8 @@ name: Product site
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/**/*.md'
|
||||
- 'gh-pages-output/**'
|
||||
- 'web-pages/product-site/**'
|
||||
- 'web-pages/scripts/**'
|
||||
- 'web-pages/nginx/**'
|
||||
@@ -10,6 +12,8 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'docs/**/*.md'
|
||||
- 'gh-pages-output/**'
|
||||
- 'web-pages/product-site/**'
|
||||
- 'web-pages/scripts/**'
|
||||
- 'web-pages/nginx/**'
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#quick-start">Quick Start</a> · <a href="./examples/colab/">Colab</a> · <a href="#benchmark">Benchmark</a> · <a href="./docs/model_selection.md">Model selection</a> · <a href="./docs/migration_from_whisper.md">Migration guide</a> · <a href="./docs/use_case_showcase.md">Use cases</a> · <a href="./docs/community_projects.md">Community integrations</a> · <a href="./docs/deployment_matrix.md">Deployment matrix</a> · <a href="https://www.funasr.com/">Deployment hub</a> · <a href="./docs/troubleshooting.md">Troubleshooting</a> · <a href="#model-zoo">Models</a> · <a href="https://modelscope.github.io/FunASR/agent.html">Agent Integration</a> · <a href="./integrations/openclaw/">OpenClaw</a> · <a href="https://modelscope.github.io/FunASR/">Docs</a> · <a href="./CONTRIBUTING.md">Contribute</a>
|
||||
<a href="#quick-start">Quick Start</a> · <a href="./docs/model_selection.md">Model selection</a> · <a href="#model-zoo">Models</a> · <a href="./docs/deployment_matrix.md">Deployment matrix</a> · <a href="https://www.funasr.com/">Deployment hub</a> · <a href="https://www.funasr.com/en/docs/">Docs</a> · <a href="#benchmark">Benchmark</a> · <a href="./CONTRIBUTING.md">Contribute</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#クイックスタート">クイックスタート</a> · <a href="./examples/colab/README_ja.md">Colab</a> · <a href="./docs/model_selection_ja.md">モデル選択</a> · <a href="#ベンチマーク">ベンチマーク</a> · <a href="./docs/migration_from_whisper.md">Migration guide</a> · <a href="./docs/use_case_showcase.md">Use cases</a> · <a href="./docs/deployment_matrix_ja.md">Deployment matrix</a> · <a href="#モデル一覧">モデル一覧</a> · <a href="https://modelscope.github.io/FunASR/agent.html">Agent連携</a> · <a href="https://modelscope.github.io/FunASR/">ドキュメント</a>
|
||||
<a href="#クイックスタート">クイックスタート</a> · <a href="./docs/model_selection_ja.md">モデル選択</a> · <a href="#モデル一覧">モデル一覧</a> · <a href="./docs/deployment_matrix_ja.md">デプロイ方式</a> · <a href="https://www.funasr.com/en/">デプロイセンター</a> · <a href="https://www.funasr.com/en/docs/">ドキュメント</a> · <a href="#ベンチマーク">ベンチマーク</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#빠른-시작">빠른 시작</a> · <a href="./examples/colab/README_ko.md">Colab</a> · <a href="./docs/model_selection_ko.md">모델 선택</a> · <a href="#벤치마크">벤치마크</a> · <a href="./docs/migration_from_whisper.md">Migration guide</a> · <a href="./docs/use_case_showcase.md">Use cases</a> · <a href="./docs/deployment_matrix_ko.md">Deployment matrix</a> · <a href="#모델-목록">모델 목록</a> · <a href="https://modelscope.github.io/FunASR/agent.html">Agent 연동</a> · <a href="https://modelscope.github.io/FunASR/">문서</a>
|
||||
<a href="#빠른-시작">빠른 시작</a> · <a href="./docs/model_selection_ko.md">모델 선택</a> · <a href="#모델-목록">모델 목록</a> · <a href="./docs/deployment_matrix_ko.md">배포 방식</a> · <a href="https://www.funasr.com/en/">배포 센터</a> · <a href="https://www.funasr.com/en/docs/">문서</a> · <a href="#벤치마크">벤치마크</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#快速开始">快速开始</a> · <a href="./examples/colab/README_zh.md">Colab</a> · <a href="#性能评测">性能评测</a> · <a href="./docs/model_selection_zh.md">模型选择</a> · <a href="./docs/migration_from_whisper_zh.md">迁移指南</a> · <a href="./docs/use_case_showcase_zh.md">场景速览</a> · <a href="./docs/community_projects_zh.md">社区集成</a> · <a href="./docs/deployment_matrix_zh.md">部署选型</a> · <a href="https://www.funasr.com/">部署中心</a> · <a href="./docs/troubleshooting_zh.md">排障 FAQ</a> · <a href="#模型列表">模型列表</a> · <a href="https://modelscope.github.io/FunASR/agent.html">Agent 集成</a> · <a href="./integrations/openclaw/">OpenClaw</a> · <a href="https://modelscope.github.io/FunASR/zh/">文档</a> · <a href="./CONTRIBUTING.md">贡献</a>
|
||||
<a href="#快速开始">快速开始</a> · <a href="./docs/model_selection_zh.md">模型选择</a> · <a href="#模型列表">模型列表</a> · <a href="./docs/deployment_matrix_zh.md">部署选型</a> · <a href="https://www.funasr.com/">部署中心</a> · <a href="https://www.funasr.com/docs/">文档中心</a> · <a href="#性能评测">性能评测</a> · <a href="./CONTRIBUTING.md">贡献</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
+49
-3
@@ -1,6 +1,52 @@
|
||||
# FunASR document generation
|
||||
# FunASR Documentation
|
||||
|
||||
[中文文档](https://www.funasr.com/docs/) |
|
||||
[English documentation](https://www.funasr.com/en/docs/) |
|
||||
[Model Zoo](../model_zoo/modelscope_models.md) |
|
||||
[Releases](https://github.com/modelscope/FunASR/releases)
|
||||
|
||||
## Start Here
|
||||
|
||||
| Your task | Guide |
|
||||
| --- | --- |
|
||||
| Choose a model, language, or timestamp capability | [Model selection](model_selection.md) / [中文](model_selection_zh.md) |
|
||||
| Run the first transcription | [Quickstart](https://www.funasr.com/en/quickstart.html) / [CLI](cli.md) |
|
||||
| Move from Whisper or a cloud API | [Migration](migration_from_whisper.md) / [中文](migration_from_whisper_zh.md) |
|
||||
|
||||
## Models and Deployment
|
||||
|
||||
| Your task | Guide |
|
||||
| --- | --- |
|
||||
| Compare CPU, GPU, realtime and API runtimes | [Deployment matrix](deployment_matrix.md) / [中文](deployment_matrix_zh.md) |
|
||||
| Transcribe and diarize with third-party MOSS | [MOSS](moss_transcribe_diarize.md) / [中文](moss_transcribe_diarize_zh.md) |
|
||||
| Accelerate with the FunASR vLLM split engine | [vLLM](vllm_guide.md) / [中文](vllm_guide_zh.md) |
|
||||
| Evaluate native vLLM serving | [Validation record](vllm_native_funasr_validation.md) |
|
||||
| Deploy llama.cpp, TensorRT, Docker or Kubernetes | [Deployment manuals](https://www.funasr.com/en/deploy/) |
|
||||
|
||||
## Evaluate and Operate
|
||||
|
||||
- [Performance methodology](benchmark/rtf_reproducibility.md) and [realtime WebSocket benchmarks](benchmark/realtime_ws_benchmark.md)
|
||||
- [Troubleshooting](troubleshooting.md) / [中文](troubleshooting_zh.md)
|
||||
- [Use cases](use_case_showcase.md), [community integrations](community_projects.md), and [repository responsibilities](repository_roles.md)
|
||||
|
||||
The product-site documentation is rendered from these Markdown sources. Edit the
|
||||
source once; the site build updates article content, navigation and local search.
|
||||
The catalogue lives in [documentation.json](../web-pages/product-site/data/documentation.json).
|
||||
Model weights retain their individual licenses; the FunASR toolkit is MIT licensed.
|
||||
|
||||
## Build the Product Documentation
|
||||
|
||||
From the repository root:
|
||||
|
||||
```sh
|
||||
python -m pip install -r web-pages/product-site/requirements-site.txt
|
||||
python web-pages/product-site/build.py --output /tmp/funasr-docs
|
||||
python web-pages/product-site/validate.py /tmp/funasr-docs
|
||||
python -m http.server --directory /tmp/funasr-docs 8000
|
||||
```
|
||||
|
||||
## Build the Sphinx Reference
|
||||
|
||||
## Generate HTML
|
||||
For convenience, we provide users with the ability to generate local HTML manually.
|
||||
|
||||
First, you should install the following packages, which is required for building HTML:
|
||||
@@ -16,4 +62,4 @@ cd docs
|
||||
make html
|
||||
```
|
||||
|
||||
The generated files are all contained in the "FunASR/docs/_build" directory. You can access the FunASR documentation by simply opening the "html/index.html" file in your browser from this directory.
|
||||
The generated files are all contained in the "FunASR/docs/_build" directory. You can access the FunASR documentation by simply opening the "html/index.html" file in your browser from this directory.
|
||||
|
||||
+46
-105
@@ -1,135 +1,76 @@
|
||||
.. Funasr documentation master file, created by
|
||||
sphinx-quickstart on Tues Dec 6 19:05:00 2022.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
FunASR Documentation
|
||||
====================
|
||||
|
||||
FunASR: A Fundamental End-to-End Speech Recognition Toolkit
|
||||
============================================================
|
||||
.. image:: ./images/funasr_logo.jpg
|
||||
Build speech applications with the right model, a reproducible inference path,
|
||||
and explicit deployment boundaries.
|
||||
|
||||
FunASR hopes to build a bridge between academic research and industrial applications on speech recognition. By supporting the training & finetuning of the industrial-grade speech recognition model released on `ModelScope <https://www.modelscope.cn/models?page=1&tasks=auto-speech-recognition>`_, researchers and developers can conduct research and production of speech recognition models more conveniently, and promote the development of speech recognition ecology. ASR for Fun!
|
||||
|
||||
Overview
|
||||
============================================================
|
||||
.. image:: ./images/funasr_overview.png
|
||||
The `product documentation <https://www.funasr.com/en/docs/>`_ and
|
||||
`Chinese documentation <https://www.funasr.com/docs/>`_ provide a searchable
|
||||
entry to these guides. Source Markdown remains in this repository.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Installation
|
||||
|
||||
./installation/installation.md
|
||||
./installation/docker.md
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 5
|
||||
:caption: Quick Start
|
||||
|
||||
./funasr/quick_start.md
|
||||
:caption: Get Started
|
||||
|
||||
installation/installation
|
||||
installation/docker
|
||||
model_selection
|
||||
model_selection_zh
|
||||
cli
|
||||
migration_from_whisper
|
||||
migration_from_whisper_zh
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Academic Egs
|
||||
:caption: Models and Deployment
|
||||
|
||||
./academic_recipe/asr_recipe.md
|
||||
./academic_recipe/punc_recipe.md
|
||||
./academic_recipe/vad_recipe.md
|
||||
./academic_recipe/sv_recipe.md
|
||||
./academic_recipe/sd_recipe.md
|
||||
|
||||
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: ModelScope Egs
|
||||
|
||||
./modelscope_pipeline/quick_start.md
|
||||
./egs_modelscope/asr/TEMPLATE/README.md
|
||||
./egs_modelscope/vad/TEMPLATE/README.md
|
||||
./egs_modelscope/punctuation/TEMPLATE/README.md
|
||||
./egs_modelscope/tp/TEMPLATE/README.md
|
||||
./modelscope_pipeline/sv_pipeline.md
|
||||
./modelscope_pipeline/sd_pipeline.md
|
||||
./modelscope_pipeline/itn_pipeline.md
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Huggingface Egs
|
||||
|
||||
Undo
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Model Zoo
|
||||
|
||||
./model_zoo/modelscope_models.md
|
||||
./model_zoo/huggingface_models.md
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Runtime and Service
|
||||
|
||||
./deployment_matrix.md
|
||||
./deployment_matrix_zh.md
|
||||
deployment_matrix
|
||||
deployment_matrix_zh
|
||||
./moss_transcribe_diarize.md
|
||||
./moss_transcribe_diarize_zh.md
|
||||
./runtime/readme.md
|
||||
./runtime/docs/SDK_tutorial_online.md
|
||||
./runtime/docs/SDK_tutorial.md
|
||||
./runtime/html5/readme.md
|
||||
|
||||
|
||||
vllm_guide
|
||||
vllm_guide_zh
|
||||
vllm_native_funasr_validation
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Benchmark and Leaderboard
|
||||
|
||||
./benchmark/rtf_reproducibility.md
|
||||
./benchmark/realtime_ws_benchmark.md
|
||||
./benchmark/benchmark_onnx.md
|
||||
./benchmark/benchmark_onnx_cpp.md
|
||||
./benchmark/benchmark_libtorch.md
|
||||
./benchmark/benchmark_pipeline_cer.md
|
||||
:caption: Evaluate and Operate
|
||||
|
||||
benchmark/rtf_reproducibility
|
||||
benchmark/realtime_ws_benchmark
|
||||
troubleshooting
|
||||
troubleshooting_zh
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Funasr Library
|
||||
:caption: Applications and Ecosystem
|
||||
|
||||
./reference/build_task.md
|
||||
use_case_showcase
|
||||
use_case_showcase_zh
|
||||
community_projects
|
||||
community_projects_zh
|
||||
repository_roles
|
||||
repository_roles_zh
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Papers
|
||||
:caption: Reference
|
||||
|
||||
./reference/papers.md
|
||||
reference/build_task
|
||||
reference/papers
|
||||
reference/application
|
||||
reference/FQA
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Application
|
||||
Model weights and runtime choices
|
||||
--------------------------------
|
||||
|
||||
./model_selection.md
|
||||
./model_selection_zh.md
|
||||
./migration_from_whisper.md
|
||||
./migration_from_whisper_zh.md
|
||||
./cli.md
|
||||
./use_case_showcase.md
|
||||
./use_case_showcase_zh.md
|
||||
./community_projects.md
|
||||
./reference/application.md
|
||||
- `Model Zoo <https://github.com/modelscope/FunASR/tree/main/model_zoo>`_
|
||||
- `Production deployment manuals <https://www.funasr.com/en/deploy/>`_
|
||||
- `Training examples <https://github.com/modelscope/FunASR/tree/main/examples>`_
|
||||
- `Release assets <https://github.com/modelscope/FunASR/releases>`_
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: FAQ and Troubleshooting
|
||||
|
||||
./reference/FQA.md
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
Indices
|
||||
-------
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Documentation and Product Website Implementation Plan
|
||||
|
||||
**Goal:** Modernize the whole FunASR documentation experience without losing existing content.
|
||||
**Architecture:** Existing Jinja generator plus source-backed Markdown catalogue and shared shell.
|
||||
**Tech Stack:** Python, Jinja2, BeautifulSoup, Python-Markdown, plain CSS/JS, Playwright.
|
||||
**Spec:** ../specs/2026-09-06-docs-product-redesign.md
|
||||
|
||||
## Sequence
|
||||
|
||||
- [x] Inventory current website, build/deploy contract and documentation sources.
|
||||
- [x] Add failing tests for source-backed docs, search, shared shell and license boundaries.
|
||||
- [x] Implement catalogue/renderer, link mapping, bilingual docs and search.
|
||||
- [x] Rework homepage, shared visual system, navigation and legacy reading surfaces.
|
||||
- [x] Reorganize source docs index, README entry points and GitHub Pages entry points.
|
||||
- [x] Run static/browser regressions and visually inspect desktop/mobile screenshots.
|
||||
- [ ] Preserve rollback bundle, sign/DCO PR, verify exact-head checks, merge and deploy.
|
||||
- [ ] Verify public routes, record release evidence and remaining content gaps.
|
||||
|
||||
## Test Commands
|
||||
|
||||
Run on ind-gpu8 from the isolated repository:
|
||||
|
||||
```sh
|
||||
python -m pytest web-pages/product-site/tests -q
|
||||
python web-pages/product-site/build.py --output /tmp/funasr-redesign-build
|
||||
python web-pages/product-site/validate.py /tmp/funasr-redesign-build
|
||||
```
|
||||
|
||||
Use the repository browser suite and an added redesign suite to verify desktop/mobile
|
||||
navigation, source-backed MOSS docs, search/no-results, copy, language peer, all old
|
||||
routes, media loading and layout overflow. Retain logs/screenshots with the release.
|
||||
@@ -0,0 +1,49 @@
|
||||
# FunASR Documentation and Product Website
|
||||
|
||||
## Intent
|
||||
|
||||
Rebuild the public documentation experience around model selection, first inference,
|
||||
deployment, and operation. The user authorized implementation and reversible
|
||||
deployment without repeated approval. Preserve existing URLs and technical scope.
|
||||
|
||||
## Architecture
|
||||
|
||||
Keep the existing Jinja/static site generator and deployment registry. Add a
|
||||
documentation catalogue whose entries point at existing Markdown sources, not
|
||||
copied articles. Render those sources into bilingual `/docs/` routes with a
|
||||
shared sidebar, section navigation, source links, and a local search index.
|
||||
Unlisted repository-relative links continue to their source on GitHub.
|
||||
The source documentation README and Sphinx index follow the same task order.
|
||||
|
||||
The homepage is a concise product/workflow entry. Models, deployments, documentation,
|
||||
ecosystem, benchmarks, blog and donors share typography, navigation and mobile
|
||||
behavior. Donors stays last. Legacy article bodies, anchors, demos and media are
|
||||
preserved while their surrounding shell is normalized during generation.
|
||||
|
||||
## Visual Direction
|
||||
|
||||
White/light-gray reading surfaces, near-black text and code surfaces, emerald
|
||||
commands and small coral accents. System sans-serif with a CJK sans fallback.
|
||||
No stock server hero, artificial performance promises, gradient decoration or
|
||||
floating section cards. Fixed-size text, restrained 8px-or-less corners,
|
||||
visible focus, reduced-motion support, readable tables and code on narrow screens.
|
||||
Use existing licensed Lucide icons and real project/audio assets.
|
||||
|
||||
## Technical Integrity
|
||||
|
||||
Keep MOSS visibly attributed to OpenMOSS, offline transcription/diarization with
|
||||
anonymous speakers, not identity recognition. Do not conflate native vLLM,
|
||||
split-engine wrappers, realtime ASR, or CPU/GPU validation coverage. Fix discovered
|
||||
stale copy from primary evidence; never expand test claims just to fit the design.
|
||||
Toolkit license comes from the current repository LICENSE, model licenses vary.
|
||||
|
||||
## Acceptance
|
||||
|
||||
1. Bilingual home, deployment hub/details, model list, docs, blog and donors work.
|
||||
2. Docs originate from repository Markdown; links, images, fenced code and tables render.
|
||||
3. Search works locally, handles no results, and needs no account or remote analytics.
|
||||
4. Mobile navigation, language peers, anchor links and copy controls work by keyboard.
|
||||
5. All existing public routes and article content survive; no broken internal links.
|
||||
6. Static tests, full site validator and browser checks at 320/390/768/1440/1920 pass.
|
||||
7. Record source commit, build checksums, previous deployment and rollback procedure
|
||||
before promotion. Verify public routes after deployment.
|
||||
@@ -0,0 +1,33 @@
|
||||
:root {color-scheme:light;font-family:Inter,Arial,"PingFang SC","Microsoft YaHei","WenQuanYi Zen Hei",sans-serif;color:#1b2723;background:#fff;line-height:1.65;letter-spacing:0}
|
||||
* {box-sizing:border-box}
|
||||
body {margin:0}
|
||||
a {color:inherit;text-decoration:none;overflow-wrap:anywhere}
|
||||
a:hover {color:#087a5b;text-decoration:underline;text-underline-offset:4px}
|
||||
a:focus-visible,button:focus-visible,input:focus-visible {outline:3px solid #cb5e48;outline-offset:4px}
|
||||
header,footer {display:flex;align-items:center;justify-content:space-between;gap:20px;max-width:1200px;margin:auto;padding:24px 32px}
|
||||
header {border-bottom:1px solid #dce3df}
|
||||
nav {display:flex;gap:24px;flex-wrap:wrap;font-size:14px}
|
||||
.brand {font-size:22px;font-weight:700}
|
||||
main {max-width:1120px;margin:auto;padding:56px 32px}
|
||||
.eyebrow {font-size:12px;color:#087a5b;margin:0 0 12px}
|
||||
h1 {font-size:56px;line-height:1.1;font-weight:600;margin:0}
|
||||
.lead {font-size:22px;color:#52645b;margin:20px 0 32px}
|
||||
form {max-width:680px;margin-bottom:48px}
|
||||
label {display:block;font-size:13px;margin-bottom:8px}
|
||||
.search {display:flex;border:1px solid #becdc5;border-radius:4px;overflow:hidden}
|
||||
input {min-width:0;flex:1;border:0;padding:14px;font:inherit;background:transparent}
|
||||
button {border:0;border-left:1px solid #becdc5;background:#eaf6f0;color:#145f49;padding:0 20px;font:inherit;cursor:pointer}
|
||||
.chapters {display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:40px 48px}
|
||||
.chapters section {border-top:2px solid #2d9272;padding-top:20px;min-width:0}
|
||||
.chapters section:nth-child(2) {border-color:#cd7965}
|
||||
.chapters section:nth-child(3) {border-color:#8a7cad}
|
||||
.chapters section:nth-child(4) {border-color:#a5a05d}
|
||||
.number {font-size:12px;color:#748b80}
|
||||
h2 {font-size:22px;font-weight:600;margin:8px 0 16px}
|
||||
.chapters a {display:block;padding:7px 0}
|
||||
.reference {border-top:1px solid #dce3df;margin-top:48px;padding-top:24px}
|
||||
.reference p {color:#52645b}
|
||||
footer {border-top:1px solid #dce3df;font-size:13px;color:#52645b;flex-wrap:wrap}
|
||||
.skip {position:absolute;top:-100px;left:20px;padding:12px;background:white;z-index:10}
|
||||
.skip:focus {top:8px}
|
||||
@media(max-width:600px) {header,footer {padding:20px}header {align-items:flex-start;flex-wrap:wrap}nav {gap:16px}main {padding:36px 20px}h1 {font-size:42px}.lead {font-size:19px}.chapters {grid-template-columns:1fr;gap:32px}h2 {font-size:20px}button {padding:0 14px}}
|
||||
+23
-353
@@ -1,359 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="FunASR: Industrial-grade speech recognition toolkit. 170x realtime on GPU, 50+ languages, speaker diarization, emotion detection, OpenAI-compatible API, vLLM acceleration, and agent integration.">
|
||||
<meta property="og:title" content="FunASR - Open-source speech understanding toolkit">
|
||||
<meta property="og:description" content="170x realtime, 50+ languages, speaker diarization, vLLM acceleration, and an OpenAI-compatible local speech API.">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://modelscope.github.io/FunASR/">
|
||||
<link rel="canonical" href="https://modelscope.github.io/FunASR/">
|
||||
<link rel="alternate" hreflang="en" href="https://modelscope.github.io/FunASR/">
|
||||
<link rel="alternate" hreflang="zh" href="https://modelscope.github.io/FunASR/zh/">
|
||||
<link rel="alternate" hreflang="ja" href="https://modelscope.github.io/FunASR/ja/">
|
||||
<link rel="alternate" hreflang="ko" href="https://modelscope.github.io/FunASR/ko/">
|
||||
<link rel="alternate" hreflang="x-default" href="https://modelscope.github.io/FunASR/">
|
||||
<meta property="og:site_name" content="FunASR">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:title" content="FunASR - Open-source speech understanding toolkit">
|
||||
<meta name="twitter:description" content="170x realtime, 50+ languages, speaker diarization, vLLM acceleration, and an OpenAI-compatible local speech API.">
|
||||
<script type="application/ld+json">
|
||||
{"@context":"https://schema.org","@type":"SoftwareSourceCode","name":"FunASR","description":"FunASR is an open-source speech recognition and speech understanding toolkit with ASR, VAD, punctuation, speaker diarization, emotion detection, vLLM acceleration, and OpenAI-compatible API examples.","url":"https://modelscope.github.io/FunASR/","codeRepository":"https://github.com/modelscope/FunASR","programmingLanguage":"Python","license":"https://github.com/modelscope/FunASR/blob/main/LICENSE","applicationCategory":"Speech recognition toolkit","operatingSystem":["Linux","macOS","Windows"]}
|
||||
</script>
|
||||
<title>FunASR - End-to-End Speech Recognition Toolkit</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>FunASR Documentation</title>
|
||||
<meta name="description" content="FunASR model selection, speech transcription, MOSS diarization, vLLM and llama.cpp deployment, and API reference.">
|
||||
<link rel="canonical" href="https://www.funasr.com/en/docs/">
|
||||
<link rel="stylesheet" href="docs-hub.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<div class="container">
|
||||
<a href="index.html" class="nav-logo">FunASR</a>
|
||||
<div class="nav-links">
|
||||
<a href="index.html" class="active">Home</a>
|
||||
<a href="tutorial.html">Tutorial</a>
|
||||
<a href="training.html">Training</a>
|
||||
<a href="model-registration.html">Develop</a>
|
||||
<a href="api.html">API</a>
|
||||
<a href="vllm.html">vLLM</a>
|
||||
<a href="agent.html">Agent</a>
|
||||
<a href="benchmark.html">Benchmark</a>
|
||||
<a href="donors.html">Donors</a>
|
||||
</div>
|
||||
|
||||
<div class="lang-dropdown"><button class="lang-btn">English</button><div class="lang-menu"><a href="index.html" class="current">English</a><a href="zh/index.html">中文</a><a href="ja/index.html">日本語</a><a href="ko/index.html">한국어</a></div></div>
|
||||
<a href="https://github.com/modelscope/FunASR" class="nav-github">GitHub</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="hero">
|
||||
<div class="container hero-content">
|
||||
<div class="hero-copy">
|
||||
<p class="eyebrow">Open-source speech understanding toolkit</p>
|
||||
<h1>FunASR</h1>
|
||||
<p>Production-ready ASR, VAD, punctuation, speaker diarization, emotion detection, and audio event recognition with one unified Python interface.</p>
|
||||
<div class="hero-badges">
|
||||
<a href="https://pypi.org/project/funasr/"><img src="https://img.shields.io/pypi/v/funasr?style=flat-square&color=2563eb" alt="PyPI"></a>
|
||||
<a href="https://github.com/modelscope/FunASR/stargazers"><img src="https://img.shields.io/github/stars/modelscope/FunASR?style=flat-square&color=b45309" alt="Stars"></a>
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-059669?style=flat-square" alt="License"></a>
|
||||
</div>
|
||||
<div class="hero-actions">
|
||||
<a href="tutorial.html" class="btn btn-white">Get started</a>
|
||||
<a href="training.html" class="btn btn-outline-white">Training guide</a>
|
||||
<a href="api.html" class="btn btn-outline-white">API reference</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-panel" aria-label="FunASR recognition preview">
|
||||
<div class="terminal-bar">
|
||||
<span class="terminal-dots"><span></span><span></span><span></span></span>
|
||||
<span>funasr-pipeline.py</span>
|
||||
</div>
|
||||
<pre>from funasr import AutoModel
|
||||
|
||||
model = AutoModel(
|
||||
model="paraformer-zh",
|
||||
vad_model="fsmn-vad",
|
||||
punc_model="ct-punc",
|
||||
spk_model="cam++",
|
||||
)
|
||||
res = model.generate(input="meeting.wav")
|
||||
print(res[0]["sentence_info"])</pre>
|
||||
<div class="hero-metrics">
|
||||
<div><strong>50+</strong><span>languages</span></div>
|
||||
<div><strong>170x</strong><span>realtime</span></div>
|
||||
<div><strong>1 API</strong><span>pipeline</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<section class="alt">
|
||||
<div class="container">
|
||||
<h2 class="section-title">What's New</h2>
|
||||
<p class="section-subtitle">The public docs now track the latest README and main-branch capabilities.</p>
|
||||
<div class="grid-3">
|
||||
<div class="card"><h3>llama.cpp / GGUF Runtime</h3><p>runtime-llamacpp-v0.1.8 adds a Linux Vulkan package for SenseVoiceSmall GGUF. Run the self-contained binary with <code>--backend vulkan</code> on supported GPUs.</p><p><a href="deployment-matrix.html#runtime-llamacpp-v0.1.8">Choose the GGUF runtime</a></p></div>
|
||||
<div class="card"><h3>MOSS-Transcribe-Diarize</h3><p>Use the third-party OpenMOSS model for offline multi-speaker meetings: one request returns text, timestamps, and anonymous speaker labels without an external VAD pipeline.</p><p><a href="moss-transcribe-diarize.html">Choose the MOSS deployment path</a></p></div>
|
||||
<div class="card"><h3>vLLM Inference Engine</h3><p>2-3x faster LLM decoding for Fun-ASR-Nano, with tensor parallel batch inference and real-time WebSocket service.</p><p><a href="vllm.html">Read the vLLM guide</a></p></div>
|
||||
<div class="card"><h3>Agent Infrastructure</h3><p><code>funasr-server</code> exposes OpenAI-compatible transcription APIs; MCP and voice-input examples connect local ASR to AI tools.</p><p><a href="agent.html">Set up Agent integration</a></p></div>
|
||||
<div class="card"><h3>Benchmark Report</h3><p>Long-form benchmark results cover SenseVoice, Paraformer, Fun-ASR-Nano, GLM-ASR, and Whisper variants on GPU and CPU.</p><p><a href="benchmark.html">View benchmark</a></p></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="alt">
|
||||
<div class="container">
|
||||
<h2 class="section-title">Private Speech API in Minutes</h2>
|
||||
<p class="section-subtitle">Run an OpenAI-compatible transcription endpoint locally, then plug it into agents, apps, and batch pipelines without sending audio to a cloud ASR provider.</p>
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<h3>Start the server</h3>
|
||||
<p>Use SenseVoice for a fast first test, or switch models after the endpoint is running.</p>
|
||||
<pre>pip install funasr fastapi uvicorn python-multipart
|
||||
funasr-server --model sensevoice --device cuda</pre>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Verify with curl</h3>
|
||||
<p>Download a public sample and call the same route used by OpenAI-compatible clients.</p>
|
||||
<pre>curl -L https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/BAC009S0764W0121.wav -o sample.wav
|
||||
curl http://localhost:8000/v1/audio/transcriptions \
|
||||
-F [email protected] \
|
||||
-F model=sensevoice \
|
||||
-F response_format=verbose_json</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-cta">
|
||||
<a href="https://colab.research.google.com/github/modelscope/FunASR/blob/main/examples/colab/funasr_quickstart.ipynb" class="btn btn-primary">Open in Colab</a>
|
||||
<a href="https://github.com/modelscope/FunASR/tree/main/examples/openai_api" class="btn btn-primary">OpenAI API example</a>
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/examples/openai_api/WORKFLOWS.md" class="btn btn-primary">Workflow recipes</a>
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/examples/openai_api/GRADIO.md" class="btn btn-primary">Gradio demo</a>
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/examples/openai_api/SECURITY.md" class="btn btn-primary">Security guide</a>
|
||||
<a href="agent.html" class="btn btn-primary">Agent integration</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="docs-band">
|
||||
<div class="container">
|
||||
<h2 class="section-title">Documentation</h2>
|
||||
<p class="section-subtitle">Start with examples, tune on your own data, extend the registry, or jump into source-linked API docs.</p>
|
||||
<div class="doc-grid">
|
||||
<a class="doc-card" href="model-selection.html">
|
||||
<span class="doc-kicker">Choose</span>
|
||||
<h3>Model Selection</h3>
|
||||
<p>Pick SenseVoice, Paraformer, Fun-ASR-Nano, streaming runtime, or OpenAI API aliases for your workload.</p>
|
||||
</a>
|
||||
<a class="doc-card" href="deployment-matrix.html">
|
||||
<span class="doc-kicker">Deploy</span>
|
||||
<h3>Deployment Matrix</h3>
|
||||
<p>Choose between Python API, OpenAI API, Docker Compose, Kubernetes, WebSocket runtime, vLLM, MCP, batch, subtitles, and Triton.</p>
|
||||
</a>
|
||||
<a class="doc-card" href="migration.html">
|
||||
<span class="doc-kicker">Compare</span>
|
||||
<h3>Migration Guide</h3>
|
||||
<p>Evaluate FunASR against Whisper or cloud ASR with feature mapping, representative benchmarks, and rollout checks.</p>
|
||||
</a>
|
||||
<a class="doc-card" href="use-cases.html">
|
||||
<span class="doc-kicker">Choose</span>
|
||||
<h3>Use Cases</h3>
|
||||
<p>Find the fastest path for private APIs, agents, streaming, vLLM, subtitles, batch jobs, and benchmarks.</p>
|
||||
</a>
|
||||
<a class="doc-card" href="tutorial.html">
|
||||
<span class="doc-kicker">Learn</span>
|
||||
<h3>Tutorial</h3>
|
||||
<p>Install FunASR, choose a model, and run common ASR, VAD, diarization, and export flows.</p>
|
||||
</a>
|
||||
<a class="doc-card" href="training.html">
|
||||
<span class="doc-kicker">Tune</span>
|
||||
<h3>Training</h3>
|
||||
<p>Prepare JSONL data, fine-tune Paraformer, SenseVoice, and Fun-ASR-Nano, then monitor runs.</p>
|
||||
</a>
|
||||
<a class="doc-card" href="model-registration.html">
|
||||
<span class="doc-kicker">Extend</span>
|
||||
<h3>Developer Guide</h3>
|
||||
<p>Understand the registry, add a model, package remote code, and avoid integration pitfalls.</p>
|
||||
</a>
|
||||
<a class="doc-card" href="vllm.html">
|
||||
<span class="doc-kicker">Accelerate</span>
|
||||
<h3>vLLM Inference</h3>
|
||||
<p>Run LLM-based ASR with vLLM, tensor parallel batch decoding, streaming SDK, and WebSocket service.</p>
|
||||
</a>
|
||||
<a class="doc-card" href="agent.html">
|
||||
<span class="doc-kicker">Integrate</span>
|
||||
<h3>Agent & API</h3>
|
||||
<p>Expose FunASR as an OpenAI-compatible endpoint, low-code workflow node, MCP tool, voice input, or subtitle generator.</p>
|
||||
</a>
|
||||
<a class="doc-card" href="benchmark.html">
|
||||
<span class="doc-kicker">Measure</span>
|
||||
<h3>Benchmark</h3>
|
||||
<p>Compare FunASR and Whisper on long-form audio, including GPU and CPU speed/CER numbers.</p>
|
||||
</a>
|
||||
<a class="doc-card" href="api.html">
|
||||
<span class="doc-kicker">Reference</span>
|
||||
<h3>API</h3>
|
||||
<p>Browse generated classes, methods, source previews, and GitHub line links.</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<h2 class="section-title">Capabilities</h2>
|
||||
<p class="section-subtitle">Everything needed for speech understanding, from raw audio segmentation to speaker-aware transcripts.</p>
|
||||
<div class="grid-3">
|
||||
<div class="card">
|
||||
<div class="card-icon">ASR</div>
|
||||
<h3>Speech Recognition</h3>
|
||||
<p>Streaming and offline ASR with VAD segmentation. Process long-form audio with a single API call.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">50+</div>
|
||||
<h3>Multilingual Models</h3>
|
||||
<p>Fun-ASR-Nano covers 31 languages and Qwen3-ASR covers 52 languages with language detection.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">SPK</div>
|
||||
<h3>Speaker Diarization</h3>
|
||||
<p>Identify who spoke when, then attach speaker labels to sentence-level ASR output.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">SFX</div>
|
||||
<h3>Emotion & Events</h3>
|
||||
<p>SenseVoice detects emotion and audio events including background music, applause, laughter, and crying.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">RTF</div>
|
||||
<h3>Low Latency</h3>
|
||||
<p>Non-autoregressive models support fast batch and realtime workloads across common deployment targets.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">OPS</div>
|
||||
<h3>Train & Deploy</h3>
|
||||
<p>Fine-tune with DeepSpeed, export to ONNX, and deploy through Docker runtime or the Python SDK.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="alt">
|
||||
<div class="container">
|
||||
<h2 class="section-title">Models</h2>
|
||||
<p class="section-subtitle">Pre-trained industrial models ready for recognition, segmentation, and speech understanding workflows.</p>
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<h3>Fun-ASR-Nano</h3>
|
||||
<p>End-to-end ASR trained on tens of millions of hours. 31 languages, dialects, accents, lyrics, timestamps, and hotwords.</p>
|
||||
<div class="card-tags">
|
||||
<span class="tag">800M</span>
|
||||
<span class="tag">31 Languages</span>
|
||||
<span class="tag">Timestamps</span>
|
||||
<span class="tag">Speaker Diarization</span>
|
||||
<span class="tag">Hotwords</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Paraformer</h3>
|
||||
<p>Non-autoregressive Chinese and English ASR with streaming and offline variants for production systems.</p>
|
||||
<div class="card-tags">
|
||||
<span class="tag">Streaming</span>
|
||||
<span class="tag">Offline</span>
|
||||
<span class="tag">Hotwords</span>
|
||||
<span class="tag">ONNX</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>SenseVoice</h3>
|
||||
<p>Multi-task speech understanding for ASR, language ID, emotion, and audio events across five languages.</p>
|
||||
<div class="card-tags">
|
||||
<span class="tag">Emotion</span>
|
||||
<span class="tag">Audio Events</span>
|
||||
<span class="tag">Speaker Diarization</span>
|
||||
<span class="tag">70ms/10s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Qwen3-ASR</h3>
|
||||
<p>LLM-based ASR with 52 languages, contextual understanding, and automatic language detection.</p>
|
||||
<div class="card-tags">
|
||||
<span class="tag">52 Languages</span>
|
||||
<span class="tag">0.6B / 1.7B</span>
|
||||
<span class="tag">Context-aware</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="container narrow">
|
||||
<h2 class="section-title">Quick Start</h2>
|
||||
<p class="section-subtitle">Install locally, or run the <a href="https://github.com/modelscope/FunASR/tree/main/examples/colab">Colab quickstart</a> first to transcribe a sample in your browser.</p>
|
||||
<pre>pip install funasr
|
||||
# Or latest: pip install git+https://github.com/modelscope/FunASR.git</pre>
|
||||
<pre>from funasr import AutoModel
|
||||
|
||||
model = AutoModel(
|
||||
model="paraformer-zh",
|
||||
vad_model="fsmn-vad",
|
||||
punc_model="ct-punc",
|
||||
spk_model="cam++",
|
||||
)
|
||||
res = model.generate(input="meeting.wav", batch_size_s=300)
|
||||
|
||||
for sent in res[0]["sentence_info"]:
|
||||
print(f"[Speaker {sent['spk']}] {sent['text']}")</pre>
|
||||
<div class="section-cta">
|
||||
<a href="https://colab.research.google.com/github/modelscope/FunASR/blob/main/examples/colab/funasr_quickstart.ipynb" class="btn btn-primary">Open in Colab</a>
|
||||
<a href="tutorial.html" class="btn btn-primary">Full tutorial</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="alt">
|
||||
<div class="container">
|
||||
<h2 class="section-title">Ecosystem</h2>
|
||||
<p class="section-subtitle">Related projects around ASR, speech understanding, video clipping, and voice generation.</p>
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<h3><a class="card-title-link" href="https://github.com/QwenAudio/Fun-ASR">Fun-ASR-Nano</a></h3>
|
||||
<p>The latest ASR large model with multilingual recognition, timestamps, speaker diarization, and hotwords.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3><a class="card-title-link" href="https://github.com/QwenAudio/SenseVoice">SenseVoice</a></h3>
|
||||
<p>Multi-task speech understanding for ASR, emotion detection, and audio event recognition.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3><a class="card-title-link" href="https://github.com/modelscope/FunClip">FunClip</a></h3>
|
||||
<p>AI video clipping powered by FunASR and LLM-assisted editing workflows.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3><a class="card-title-link" href="https://github.com/QwenAudio/CosyVoice">CosyVoice</a></h3>
|
||||
<p>Natural speech generation with multi-language, timbre, and emotion control.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>FunASR · Tongyi Lab, Alibaba Group</p>
|
||||
<p>
|
||||
<a href="https://github.com/modelscope/FunASR">GitHub</a> ·
|
||||
<a href="training.html">Training</a> ·
|
||||
<a href="use-cases.html">Use Cases</a> ·
|
||||
<a href="deployment-matrix.html">Deployment</a> ·
|
||||
<a href="api.html">API</a> ·
|
||||
<a href="vllm.html">vLLM</a> ·
|
||||
<a href="agent.html">Agent</a> ·
|
||||
<a href="benchmark.html">Benchmark</a> ·
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/MODEL_LICENSE">License</a> ·
|
||||
<a href="https://pypi.org/project/funasr/">PyPI</a> ·
|
||||
<a href="https://github.com/modelscope/FunASR/issues">Issues</a>
|
||||
</p>
|
||||
</footer>
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
<header><a class="brand" href="https://www.funasr.com/en/">FunASR</a><nav aria-label="Primary"><a href="https://www.funasr.com/en/deploy/">Deploy</a><a href="api.html">API</a><a href="zh/">中文</a><a href="https://github.com/modelscope/FunASR">GitHub</a></nav></header>
|
||||
<main id="main">
|
||||
<p class="eyebrow">DEVELOPER DOCUMENTATION</p>
|
||||
<h1>FunASR</h1>
|
||||
<p class="lead">From the first transcript to a service you can operate.</p>
|
||||
<form action="https://www.funasr.com/en/docs/" method="get" role="search"><label for="query">Search documentation</label><div class="search"><input id="query" name="q" type="search" placeholder="Models, timestamps, MOSS, vLLM..."><button type="submit">Search</button></div></form>
|
||||
<div class="chapters">
|
||||
<section><span class="number">01</span><h2>Get started</h2><a href="https://www.funasr.com/en/docs/model-selection.html">Choose a model</a><a href="tutorial.html">Python quickstart</a><a href="https://www.funasr.com/en/docs/command-line.html">Command-line inference</a><a href="https://www.funasr.com/en/docs/migration.html">Migrate from Whisper</a></section>
|
||||
<section><span class="number">02</span><h2>Models and capabilities</h2><a href="https://www.funasr.com/en/models.html">Model catalogue</a><a href="https://www.funasr.com/en/docs/moss-transcribe-diarize.html">MOSS-Transcribe-Diarize · Third-party model by OpenMOSS</a><a href="https://github.com/modelscope/FunASR/tree/main/model_zoo">Model Zoo</a><a href="model-registration.html">Register a model</a></section>
|
||||
<section><span class="number">03</span><h2>Deploy a service</h2><a href="https://www.funasr.com/en/deploy/">Deployment matrix</a><a href="https://www.funasr.com/en/deploy/vllm.html">Native vLLM</a><a href="https://www.funasr.com/en/docs/vllm.html">FunASR split-engine vLLM</a><a href="https://www.funasr.com/en/deploy/llama-cpp.html">llama.cpp and GGUF</a></section>
|
||||
<section><span class="number">04</span><h2>Operate and extend</h2><a href="https://www.funasr.com/en/docs/troubleshooting.html">Troubleshooting</a><a href="https://www.funasr.com/en/docs/benchmark-method.html">Reproducible benchmarks</a><a href="training.html">Training and fine-tuning</a><a href="api.html">Python API</a></section>
|
||||
</div>
|
||||
<section class="reference"><h2>Guides and reference</h2><p>The documentation hub uses repository Markdown as its source. Existing tutorials and API addresses remain available here.</p><nav aria-label="Reference"><a href="agent.html">Agent integrations</a><a href="use-cases.html">Use cases</a><a href="moss-transcribe-diarize.html">MOSS reference</a><a href="https://github.com/modelscope/FunASR/releases">Release assets</a><a href="ja/" lang="ja">日本語</a><a href="ko/" lang="ko">한국어</a></nav></section>
|
||||
</main>
|
||||
<footer><span>FunASR</span><a href="https://github.com/modelscope/FunASR/tree/main/docs">Edit documentation</a><a href="https://www.funasr.com/en/donors.html">Donors</a></footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+23
-353
@@ -1,359 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="FunASR 是工业级开源语音识别工具包,支持 GPU 170x 实时、50+ 语言、说话人分离、情感检测、OpenAI 兼容 API、vLLM 加速和 Agent 集成。">
|
||||
<meta property="og:title" content="FunASR - 开源语音理解工具包">
|
||||
<meta property="og:description" content="GPU 170x 实时、50+ 语言、说话人分离、vLLM 加速和本地 OpenAI 兼容语音 API。">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://modelscope.github.io/FunASR/zh/">
|
||||
<link rel="canonical" href="https://modelscope.github.io/FunASR/zh/">
|
||||
<link rel="alternate" hreflang="en" href="https://modelscope.github.io/FunASR/">
|
||||
<link rel="alternate" hreflang="zh" href="https://modelscope.github.io/FunASR/zh/">
|
||||
<link rel="alternate" hreflang="ja" href="https://modelscope.github.io/FunASR/ja/">
|
||||
<link rel="alternate" hreflang="ko" href="https://modelscope.github.io/FunASR/ko/">
|
||||
<link rel="alternate" hreflang="x-default" href="https://modelscope.github.io/FunASR/">
|
||||
<meta property="og:site_name" content="FunASR">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:title" content="FunASR - 开源语音理解工具包">
|
||||
<meta name="twitter:description" content="GPU 170x 实时、50+ 语言、说话人分离、vLLM 加速和本地 OpenAI 兼容语音 API。">
|
||||
<script type="application/ld+json">
|
||||
{"@context":"https://schema.org","@type":"SoftwareSourceCode","name":"FunASR","description":"FunASR 是开源语音识别与语音理解工具包,支持 ASR、VAD、标点恢复、说话人分离、情感检测、vLLM 加速和 OpenAI 兼容 API 示例。","url":"https://modelscope.github.io/FunASR/zh/","codeRepository":"https://github.com/modelscope/FunASR","programmingLanguage":"Python","license":"https://github.com/modelscope/FunASR/blob/main/LICENSE","applicationCategory":"Speech recognition toolkit","operatingSystem":["Linux","macOS","Windows"]}
|
||||
</script>
|
||||
<title>FunASR - 端到端语音识别工具包</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>FunASR 文档中心</title>
|
||||
<meta name="description" content="FunASR 模型选择、语音转写、MOSS 说话人分离、vLLM 与 llama.cpp 部署及 API 参考。">
|
||||
<link rel="canonical" href="https://www.funasr.com/docs/">
|
||||
<link rel="stylesheet" href="../docs-hub.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<div class="container">
|
||||
<a href="index.html" class="nav-logo">FunASR</a>
|
||||
<div class="nav-links">
|
||||
<a href="index.html" class="active">首页</a>
|
||||
<a href="tutorial.html">教程</a>
|
||||
<a href="training.html">训练</a>
|
||||
<a href="model-registration.html">开发</a>
|
||||
<a href="../api.html">API</a>
|
||||
<a href="vllm.html">vLLM</a>
|
||||
<a href="agent.html">Agent</a>
|
||||
<a href="benchmark.html">Benchmark</a>
|
||||
<a href="../donors.html">功德榜</a>
|
||||
</div>
|
||||
|
||||
<div class="lang-dropdown"><button class="lang-btn">中文</button><div class="lang-menu"><a href="../index.html">English</a><a href="index.html" class="current">中文</a><a href="../ja/index.html">日本語</a><a href="../ko/index.html">한국어</a></div></div>
|
||||
<a href="https://github.com/modelscope/FunASR" class="nav-github">GitHub</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="hero">
|
||||
<div class="container hero-content">
|
||||
<div class="hero-copy">
|
||||
<p class="eyebrow">开源语音理解工具包</p>
|
||||
<h1>FunASR</h1>
|
||||
<p>生产级语音识别、语音检测、标点恢复、说话人分离、情感检测、音频事件识别——统一的 Python 接口。</p>
|
||||
<div class="hero-badges">
|
||||
<a href="https://pypi.org/project/funasr/"><img src="https://img.shields.io/pypi/v/funasr?style=flat-square&color=2563eb" alt="PyPI"></a>
|
||||
<a href="https://github.com/modelscope/FunASR/stargazers"><img src="https://img.shields.io/github/stars/modelscope/FunASR?style=flat-square&color=b45309" alt="Stars"></a>
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-059669?style=flat-square" alt="License"></a>
|
||||
</div>
|
||||
<div class="hero-actions">
|
||||
<a href="tutorial.html" class="btn btn-white">快速开始</a>
|
||||
<a href="training.html" class="btn btn-outline-white">训练指南</a>
|
||||
<a href="../api.html" class="btn btn-outline-white">API 文档</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-panel" aria-label="FunASR 识别预览">
|
||||
<div class="terminal-bar">
|
||||
<span class="terminal-dots"><span></span><span></span><span></span></span>
|
||||
<span>funasr-pipeline.py</span>
|
||||
</div>
|
||||
<pre>from funasr import AutoModel
|
||||
|
||||
model = AutoModel(
|
||||
model="paraformer-zh",
|
||||
vad_model="fsmn-vad",
|
||||
punc_model="ct-punc",
|
||||
spk_model="cam++",
|
||||
)
|
||||
res = model.generate(input="meeting.wav")
|
||||
print(res[0]["sentence_info"])</pre>
|
||||
<div class="hero-metrics">
|
||||
<div><strong>50+</strong><span>语言</span></div>
|
||||
<div><strong>170x</strong><span>实时</span></div>
|
||||
<div><strong>1个API</strong><span>全流程</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<section class="alt">
|
||||
<div class="container">
|
||||
<h2 class="section-title">最新动态</h2>
|
||||
<p class="section-subtitle">网站文档已对齐 README 和 main 分支最新能力。</p>
|
||||
<div class="grid-3">
|
||||
<div class="card"><h3>llama.cpp / GGUF 运行时</h3><p>runtime-llamacpp-v0.1.8 新增 SenseVoiceSmall GGUF 的 Linux Vulkan 包。支持 Vulkan 的 GPU 可用自包含二进制和 <code>--backend vulkan</code> 运行。</p><p><a href="deployment-matrix.html#runtime-llamacpp-v0.1.8">选择 GGUF 运行时</a></p></div>
|
||||
<div class="card"><h3>MOSS-Transcribe-Diarize</h3><p>使用 OpenMOSS 的第三方离线模型处理多人会议:一次请求返回文本、时间戳和匿名说话人标签,不需要外挂 VAD 流水线。</p><p><a href="moss-transcribe-diarize.html">选择 MOSS 部署路径</a></p></div>
|
||||
<div class="card"><h3>vLLM 推理引擎</h3><p>Fun-ASR-Nano LLM 解码加速 2-3 倍,支持 tensor parallel 批量推理和实时 WebSocket 服务。</p><p><a href="vllm.html">阅读 vLLM 文档</a></p></div>
|
||||
<div class="card"><h3>Agent 基础设施</h3><p><code>funasr-server</code> 提供 OpenAI 兼容转写 API;MCP 和语音输入示例可把本地 ASR 接入 AI 工具。</p><p><a href="agent.html">配置 Agent 集成</a></p></div>
|
||||
<div class="card"><h3>性能评测</h3><p>长音频评测覆盖 SenseVoice、Paraformer、Fun-ASR-Nano、GLM-ASR 和 Whisper 变体,包含 GPU/CPU 结果。</p><p><a href="benchmark.html">查看评测</a></p></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="alt">
|
||||
<div class="container">
|
||||
<h2 class="section-title">几分钟部署私有语音 API</h2>
|
||||
<p class="section-subtitle">在本地启动 OpenAI 兼容的转写接口,将语音能力接入 Agent、应用和批处理流水线,无需把音频发送到云端 ASR 服务。</p>
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<h3>启动服务</h3>
|
||||
<p>先用 SenseVoice 快速验证,服务启动后也可以切换到其他模型。</p>
|
||||
<pre>pip install funasr fastapi uvicorn python-multipart
|
||||
funasr-server --model sensevoice --device cuda</pre>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>用 curl 验证</h3>
|
||||
<p>下载公开测试音频,调用与 OpenAI 兼容客户端一致的接口路径。</p>
|
||||
<pre>curl -L https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/BAC009S0764W0121.wav -o sample.wav
|
||||
curl http://localhost:8000/v1/audio/transcriptions \
|
||||
-F [email protected] \
|
||||
-F model=sensevoice \
|
||||
-F response_format=verbose_json</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-cta">
|
||||
<a href="https://colab.research.google.com/github/modelscope/FunASR/blob/main/examples/colab/funasr_quickstart.ipynb" class="btn btn-primary">打开 Colab</a>
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/examples/openai_api/README_zh.md" class="btn btn-primary">OpenAI API 示例</a>
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/examples/openai_api/WORKFLOWS_zh.md" class="btn btn-primary">工作流配方</a>
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/examples/openai_api/GRADIO_zh.md" class="btn btn-primary">Gradio 演示</a>
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/examples/openai_api/SECURITY_zh.md" class="btn btn-primary">安全指南</a>
|
||||
<a href="agent.html" class="btn btn-primary">Agent 集成</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="docs-band">
|
||||
<div class="container">
|
||||
<h2 class="section-title">文档中心</h2>
|
||||
<p class="section-subtitle">从示例开始,在自己的数据上微调,扩展模型注册表,或查阅带源码链接的 API 文档。</p>
|
||||
<div class="doc-grid">
|
||||
<a class="doc-card" href="model-selection.html">
|
||||
<span class="doc-kicker">选型</span>
|
||||
<h3>模型选择</h3>
|
||||
<p>为你的场景选择 SenseVoice、Paraformer、Fun-ASR-Nano、流式服务或 OpenAI API alias。</p>
|
||||
</a>
|
||||
<a class="doc-card" href="deployment-matrix.html">
|
||||
<span class="doc-kicker">部署</span>
|
||||
<h3>部署选型</h3>
|
||||
<p>对比 Python API、OpenAI API、Docker Compose、Kubernetes、WebSocket、vLLM、MCP、批处理、字幕和 Triton。</p>
|
||||
</a>
|
||||
<a class="doc-card" href="migration.html">
|
||||
<span class="doc-kicker">对比</span>
|
||||
<h3>迁移指南</h3>
|
||||
<p>用功能映射、代表性评测和上线检查,评估是否从 Whisper 或云端 ASR 切到 FunASR。</p>
|
||||
</a>
|
||||
<a class="doc-card" href="use-cases.html">
|
||||
<span class="doc-kicker">选型</span>
|
||||
<h3>场景速览</h3>
|
||||
<p>快速找到私有 API、Agent、流式、vLLM、字幕、批处理和评测的最佳入口。</p>
|
||||
</a>
|
||||
<a class="doc-card" href="tutorial.html">
|
||||
<span class="doc-kicker">学习</span>
|
||||
<h3>使用教程</h3>
|
||||
<p>安装 FunASR,选择模型,运行语音识别、VAD、说话人分离和导出流程。</p>
|
||||
</a>
|
||||
<a class="doc-card" href="training.html">
|
||||
<span class="doc-kicker">微调</span>
|
||||
<h3>模型训练</h3>
|
||||
<p>准备 JSONL 数据,微调 Paraformer、SenseVoice、Fun-ASR-Nano,监控训练过程。</p>
|
||||
</a>
|
||||
<a class="doc-card" href="model-registration.html">
|
||||
<span class="doc-kicker">扩展</span>
|
||||
<h3>开发指南</h3>
|
||||
<p>理解注册表机制,添加新模型,打包独立仓库代码,避免集成陷阱。</p>
|
||||
</a>
|
||||
<a class="doc-card" href="vllm.html">
|
||||
<span class="doc-kicker">加速</span>
|
||||
<h3>vLLM 推理</h3>
|
||||
<p>使用 vLLM 加速 LLM-based ASR,支持多卡批量解码、流式 SDK 和 WebSocket 服务。</p>
|
||||
</a>
|
||||
<a class="doc-card" href="agent.html">
|
||||
<span class="doc-kicker">集成</span>
|
||||
<h3>Agent 与 API</h3>
|
||||
<p>将 FunASR 暴露为 OpenAI 兼容接口、低代码工作流节点、MCP 工具、语音输入法或字幕生成器。</p>
|
||||
</a>
|
||||
<a class="doc-card" href="benchmark.html">
|
||||
<span class="doc-kicker">评测</span>
|
||||
<h3>性能评测</h3>
|
||||
<p>对比 FunASR 与 Whisper 在长音频上的 GPU/CPU 速度和 CER。</p>
|
||||
</a>
|
||||
<a class="doc-card" href="../api.html">
|
||||
<span class="doc-kicker">参考</span>
|
||||
<h3>API</h3>
|
||||
<p>浏览自动生成的类、方法文档,查看源码预览和 GitHub 行号链接。</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<h2 class="section-title">核心能力</h2>
|
||||
<p class="section-subtitle">语音理解所需的一切——从音频分段到带说话人标注的转写结果。</p>
|
||||
<div class="grid-3">
|
||||
<div class="card">
|
||||
<div class="card-icon">ASR</div>
|
||||
<h3>语音识别</h3>
|
||||
<p>支持流式和离线 ASR,内置 VAD 分段。单次 API 调用即可处理数小时的长音频。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">50+</div>
|
||||
<h3>多语言模型</h3>
|
||||
<p>Fun-ASR-Nano 覆盖 31 种语言含中文方言,Qwen3-ASR 覆盖 52 种语言并自动检测语种。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">SPK</div>
|
||||
<h3>说话人分离</h3>
|
||||
<p>识别"谁在什么时候说了什么",将说话人标签附加到句子级 ASR 输出。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">SFX</div>
|
||||
<h3>情感与音频事件</h3>
|
||||
<p>SenseVoice 可检测语音情感和音频事件——背景音乐、掌声、笑声、哭声。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">RTF</div>
|
||||
<h3>低延迟</h3>
|
||||
<p>非自回归模型支持高效批处理和实时场景,可部署在各种硬件上。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">OPS</div>
|
||||
<h3>训练与部署</h3>
|
||||
<p>支持 DeepSpeed 微调、ONNX 导出、Docker 运行时和 Python SDK 部署。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="alt">
|
||||
<div class="container">
|
||||
<h2 class="section-title">模型</h2>
|
||||
<p class="section-subtitle">预训练工业级模型,用于语音识别、音频分段和语音理解流水线。</p>
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<h3>Fun-ASR-Nano</h3>
|
||||
<p>基于数千万小时数据训练的端到端 ASR。支持 31 种语言、方言、歌词、时间戳和热词。</p>
|
||||
<div class="card-tags">
|
||||
<span class="tag">8亿参数</span>
|
||||
<span class="tag">31种语言</span>
|
||||
<span class="tag">时间戳</span>
|
||||
<span class="tag">说话人分离</span>
|
||||
<span class="tag">热词</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Paraformer</h3>
|
||||
<p>非自回归中英文语音识别,提供流式和离线版本,适用于生产系统。</p>
|
||||
<div class="card-tags">
|
||||
<span class="tag">流式</span>
|
||||
<span class="tag">离线</span>
|
||||
<span class="tag">热词</span>
|
||||
<span class="tag">ONNX</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>SenseVoice</h3>
|
||||
<p>多任务语音理解:语音识别、语种识别、情感检测、音频事件检测,支持 5 种语言。</p>
|
||||
<div class="card-tags">
|
||||
<span class="tag">情感</span>
|
||||
<span class="tag">音频事件</span>
|
||||
<span class="tag">说话人分离</span>
|
||||
<span class="tag">70ms/10s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Qwen3-ASR</h3>
|
||||
<p>基于大语言模型的语音识别,支持 52 种语言,具备上下文理解和自动语种检测能力。</p>
|
||||
<div class="card-tags">
|
||||
<span class="tag">52种语言</span>
|
||||
<span class="tag">0.6B / 1.7B</span>
|
||||
<span class="tag">上下文感知</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="container narrow">
|
||||
<h2 class="section-title">快速开始</h2>
|
||||
<p class="section-subtitle">可以本地安装,也可以先运行 <a href="https://github.com/modelscope/FunASR/blob/main/examples/colab/README_zh.md">Colab 快速体验</a>,在浏览器里转写样例音频。</p>
|
||||
<pre>pip install funasr
|
||||
# 或安装最新版:pip install git+https://github.com/modelscope/FunASR.git</pre>
|
||||
<pre>from funasr import AutoModel
|
||||
|
||||
model = AutoModel(
|
||||
model="paraformer-zh",
|
||||
vad_model="fsmn-vad",
|
||||
punc_model="ct-punc",
|
||||
spk_model="cam++",
|
||||
)
|
||||
res = model.generate(input="meeting.wav", batch_size_s=300)
|
||||
|
||||
for sent in res[0]["sentence_info"]:
|
||||
print(f"[说话人 {sent['spk']}] {sent['text']}")</pre>
|
||||
<div class="section-cta">
|
||||
<a href="https://colab.research.google.com/github/modelscope/FunASR/blob/main/examples/colab/funasr_quickstart.ipynb" class="btn btn-primary">打开 Colab</a>
|
||||
<a href="tutorial.html" class="btn btn-primary">完整教程</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="alt">
|
||||
<div class="container">
|
||||
<h2 class="section-title">生态系统</h2>
|
||||
<p class="section-subtitle">围绕语音识别、语音理解、视频剪辑、语音合成的相关项目。</p>
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<h3><a class="card-title-link" href="https://github.com/QwenAudio/Fun-ASR">Fun-ASR-Nano</a></h3>
|
||||
<p>最新的语音识别大模型,支持多语言识别、时间戳、说话人分离和热词定制。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3><a class="card-title-link" href="https://github.com/QwenAudio/SenseVoice">SenseVoice</a></h3>
|
||||
<p>多任务语音理解:语音识别、情感检测、音频事件识别。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3><a class="card-title-link" href="https://github.com/modelscope/FunClip">FunClip</a></h3>
|
||||
<p>基于 FunASR 的 AI 视频剪辑,支持大模型辅助的智能编辑流程。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3><a class="card-title-link" href="https://github.com/QwenAudio/CosyVoice">CosyVoice</a></h3>
|
||||
<p>自然语音生成,支持多语言、音色和情感控制。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>FunASR · 通义实验室,阿里巴巴集团</p>
|
||||
<p>
|
||||
<a href="https://github.com/modelscope/FunASR">GitHub</a> ·
|
||||
<a href="training.html">训练</a> ·
|
||||
<a href="use-cases.html">场景速览</a> ·
|
||||
<a href="deployment-matrix.html">部署选型</a> ·
|
||||
<a href="../api.html">API</a> ·
|
||||
<a href="vllm.html">vLLM</a> ·
|
||||
<a href="agent.html">Agent</a> ·
|
||||
<a href="benchmark.html">Benchmark</a> ·
|
||||
<a href="https://github.com/modelscope/FunASR/blob/main/MODEL_LICENSE">许可证</a> ·
|
||||
<a href="https://pypi.org/project/funasr/">PyPI</a> ·
|
||||
<a href="https://github.com/modelscope/FunASR/issues">问题反馈</a>
|
||||
</p>
|
||||
</footer>
|
||||
<a class="skip" href="#main">跳到正文</a>
|
||||
<header><a class="brand" href="https://www.funasr.com/">FunASR</a><nav aria-label="主导航"><a href="https://www.funasr.com/deploy/">部署</a><a href="../api.html">API</a><a href="../">EN</a><a href="https://github.com/modelscope/FunASR">GitHub</a></nav></header>
|
||||
<main id="main">
|
||||
<p class="eyebrow">开发者文档</p>
|
||||
<h1>FunASR</h1>
|
||||
<p class="lead">从第一次转写,到稳定运行的语音服务。</p>
|
||||
<form action="https://www.funasr.com/docs/" method="get" role="search"><label for="query">搜索文档</label><div class="search"><input id="query" name="q" type="search" placeholder="模型、时间戳、MOSS、vLLM..."><button type="submit">搜索</button></div></form>
|
||||
<div class="chapters">
|
||||
<section><span class="number">01</span><h2>开始使用</h2><a href="https://www.funasr.com/docs/model-selection.html">选择模型</a><a href="tutorial.html">Python 快速开始</a><a href="https://www.funasr.com/docs/command-line.html">命令行推理</a><a href="https://www.funasr.com/docs/migration.html">从 Whisper 迁移</a></section>
|
||||
<section><span class="number">02</span><h2>模型与能力</h2><a href="https://www.funasr.com/models.html">模型目录</a><a href="https://www.funasr.com/docs/moss-transcribe-diarize.html">MOSS-Transcribe-Diarize · OpenMOSS 第三方模型</a><a href="https://github.com/modelscope/FunASR/tree/main/model_zoo">Model Zoo</a><a href="../model-registration.html">注册模型</a></section>
|
||||
<section><span class="number">03</span><h2>部署服务</h2><a href="https://www.funasr.com/deploy/">部署矩阵</a><a href="https://www.funasr.com/deploy/vllm.html">原生 vLLM</a><a href="https://www.funasr.com/docs/vllm.html">FunASR split-engine vLLM</a><a href="https://www.funasr.com/deploy/llama-cpp.html">llama.cpp 与 GGUF</a></section>
|
||||
<section><span class="number">04</span><h2>运维与扩展</h2><a href="https://www.funasr.com/docs/troubleshooting.html">常见问题与排障</a><a href="https://www.funasr.com/docs/benchmark-method.html">可复现性能评测</a><a href="../training.html">训练与微调</a><a href="../api.html">Python API</a></section>
|
||||
</div>
|
||||
<section class="reference"><h2>教程与参考</h2><p>统一文档中心从仓库 Markdown 生成。本站原有教程和 API 地址继续保留。</p><nav aria-label="参考资料"><a href="../agent.html">Agent 集成</a><a href="use-cases.html">应用场景</a><a href="moss-transcribe-diarize.html">MOSS 参考</a><a href="https://github.com/modelscope/FunASR/releases">发布产物</a><a href="../ja/" lang="ja">日本語</a><a href="../ko/" lang="ko">한국어</a></nav></section>
|
||||
</main>
|
||||
<footer><span>FunASR</span><a href="https://github.com/modelscope/FunASR/tree/main/docs">编辑文档</a><a href="https://www.funasr.com/donors.html">功德榜</a></footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# FunASR product website and documentation
|
||||
|
||||
This is the source for www.funasr.com. It uses Jinja templates and a static
|
||||
Python build; no browser framework or runtime application server is required.
|
||||
|
||||
## Content ownership
|
||||
|
||||
- `data/documentation.json`: task groups, bilingual titles and Markdown sources.
|
||||
- Repository `docs/*.md`: documentation body, shared with GitHub readers.
|
||||
- `data/deployments.json`: versions, hardware, evidence and operational limits.
|
||||
- `templates/`: generated homepage, deployment manuals and documentation shell.
|
||||
- `assets/css/experience.css`: shared product and legacy reading experience.
|
||||
- `legacy/`: preserved public routes, content and demo assets. Do not delete or
|
||||
replace this snapshot with a partial output tree.
|
||||
- Repository `gh-pages-output/index.html` and `zh/index.html`: GitHub Pages entry
|
||||
points. Their existing API/tutorial routes remain available.
|
||||
|
||||
The build emits search indexes, maps source-relative document links, preserves
|
||||
Unicode heading anchors and fingerprints all product assets. Historical
|
||||
benchmark evidence must retain its exact tested model/runtime/hardware; new
|
||||
upstream support is not evidence that old tests covered a new checkpoint.
|
||||
|
||||
## Validate
|
||||
|
||||
```sh
|
||||
python -m pip install -r web-pages/product-site/requirements-site.txt
|
||||
python -m pytest web-pages/product-site/tests -q
|
||||
python web-pages/product-site/build.py --output /tmp/funasr-site
|
||||
python web-pages/product-site/validate.py /tmp/funasr-site
|
||||
```
|
||||
|
||||
Browser tests live in `tests/browser/`. Install their pinned npm dependencies
|
||||
and Chromium, then run `npm test` from that directory. The suite owns its local
|
||||
HTTP server and rejects reuse of an occupied port.
|
||||
|
||||
Production releases use `web-pages/scripts/deploy-product-site.sh`; see
|
||||
`docs/operations/funasr-com-site-release.md` for backup, atomic switching and
|
||||
rollback. Only publish a tested artifact, never a partially built output.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Homepage speech sample
|
||||
|
||||
The audio is the public Chinese sample used by FunASR quickstart examples:
|
||||
https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/asr_example_zh.wav
|
||||
|
||||
- Duration: 5.5466875 seconds.
|
||||
- SHA256: a1bd32dc78493c123f9625a66deee562aed2895f53fbc39f2cca3be7e6f4f20f
|
||||
- The homepage waveform is plotted from the actual sample, not generated speech.
|
||||
- Model and sample rights remain with their respective owners; the repository's
|
||||
software license is not a separate license grant for third-party media.
|
||||
Binary file not shown.
@@ -0,0 +1,261 @@
|
||||
/* Shared reading and navigation system for generated guides and legacy articles. */
|
||||
:root {
|
||||
--ink: #191d1c;
|
||||
--muted: #626b68;
|
||||
--line: #e0e6e3;
|
||||
--soft: #f5f7f6;
|
||||
--paper: #fff;
|
||||
--blue: #087f5b;
|
||||
--blue-dark: #066347;
|
||||
--green: #087f5b;
|
||||
--primary: #087f5b;
|
||||
--primary-dark: #066347;
|
||||
--text: #191d1c;
|
||||
--text-soft: #626b68;
|
||||
--text-muted: #626b68;
|
||||
--border: #e0e6e3;
|
||||
--radius: 8px;
|
||||
--font-reading: -apple-system, BlinkMacSystemFont, "Inter", "Noto Sans CJK SC", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", "WenQuanYi Zen Hei", Arial, sans-serif;
|
||||
font-family: var(--font-reading);
|
||||
}
|
||||
body, button, input, select, textarea { font-family: var(--font-reading); letter-spacing: 0; }
|
||||
body { -webkit-font-smoothing: antialiased; }
|
||||
h1, h2, h3, h4 { font-family: var(--font-reading); text-wrap: balance; letter-spacing: 0; }
|
||||
body h1 { font-size: 42px; line-height: 1.2; font-weight: 740; }
|
||||
body h2 { font-size: 28px; line-height: 1.35; }
|
||||
p { text-wrap: pretty; }
|
||||
pre, code { font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; }
|
||||
button, a { -webkit-tap-highlight-color: transparent; }
|
||||
.site-header { height: 72px; min-height: 72px; padding: 0 max(24px, calc((100% - 1328px) / 2)); background: #fffffff5; }
|
||||
.brand { font-size: 20px; font-weight: 760; }
|
||||
.brand-mark { width: 28px; height: 28px; background: #191d1c; color: #7eebbb; border-radius: 7px; }
|
||||
.primary-navigation a { font-size: 13px; padding: 10px 9px; }
|
||||
.primary-navigation a[aria-current=page] { color: var(--blue); border-bottom-color: var(--blue); }
|
||||
.section { max-width: 1200px; padding: 48px 0; margin: 0 auto; border-bottom: 1px solid var(--line); }
|
||||
.section-heading { margin-bottom: 26px; max-width: 760px; }
|
||||
.section-heading h2 { margin: 8px 0 12px; }
|
||||
.section-heading p:not(.eyebrow) { font-size: 15px; line-height: 1.8; }
|
||||
.eyebrow { color: var(--blue); font-size: 11px; font-weight: 700; letter-spacing: 0; }
|
||||
.button { border-radius: 6px; font-size: 14px; min-height: 44px; padding: 10px 18px; }
|
||||
.button-primary { background: var(--blue); border-color: var(--blue); color: white; }
|
||||
.button-primary:hover { background: #066347; border-color: #066347; }
|
||||
.hero { min-height: 0; height: 560px; background: #171d1b; color: #fff; overflow: hidden; }
|
||||
.hero-image { position: absolute; inset: auto 0 0; width: 100%; height: 120px; object-fit: cover; opacity: 1; }
|
||||
.hero-content { position: relative; width: min(1200px, calc(100% - 48px)); max-width: none; padding: 48px 0 0; margin: 0 auto; }
|
||||
.hero .eyebrow { color: #89dbb6; margin: 0 0 10px; }
|
||||
.hero h1 { font-size: 72px; line-height: 1.12; margin: 0 0 12px; color: white; }
|
||||
.hero .hero-copy { max-width: 900px; font-size: 27px; line-height: 1.45; font-weight: 530; margin: 0 0 10px; color: #f8fbf9; }
|
||||
.hero-description { max-width: 660px; font-size: 16px; line-height: 1.7; color: #afbdb7; margin: 0 0 24px; }
|
||||
.hero .button-primary { background: #8fefc2; border-color: #8fefc2; color: #103328; }
|
||||
.hero .button-primary img { filter: none; }
|
||||
.hero .button-secondary { color: #fff; background: #ffffff08; border-color: #55625c; }
|
||||
.hero .button-secondary img { filter: invert(1); }
|
||||
.hero-content > .audio-proof { max-width: none; }
|
||||
.audio-proof { position: relative; display: flex; justify-content: space-between; gap: 24px; align-items: center; padding: 18px 0; margin-top: 24px; border-top: 1px solid #506359; }
|
||||
.sample-label { color: #8fefc2; font: 11px monospace; }
|
||||
.audio-proof p { margin: 8px 0 0; color: #f8fbf9; font-size: 15px; }
|
||||
.audio-proof audio { width: 280px; height: 40px; flex-shrink: 0; }
|
||||
.repository-router { padding-top: 40px; }
|
||||
.repository-router-links { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px; }
|
||||
.repository-router-links a { min-height: 112px; padding: 18px 0; border-top: 2px solid #087f5b; }
|
||||
.repository-router-links a:nth-child(2) { border-color: #715dba; }
|
||||
.repository-router-links a:nth-child(3) { border-color: #c55d51; }
|
||||
.repository-router-links a:nth-child(4) { border-color: #b48125; }
|
||||
.repository-router-links span { font-size: 15px; color: #191d1c; font-weight: 700; }
|
||||
.repository-router-links small { font-size: 10px; font-weight: 400; color: var(--muted); }
|
||||
.repository-router-links strong { font-size: 13px; font-weight: 450; color: var(--muted); }
|
||||
.knowledge-entry { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 64px; align-items: start; }
|
||||
.knowledge-entry .learning-paths { grid-column: 1 / -1; }
|
||||
.learning-paths { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 24px; margin-top: 18px; }
|
||||
.learning-paths a { text-decoration: none; display: grid; gap: 8px; padding: 18px 0; border-top: 1px solid var(--line); }
|
||||
.learning-paths a span { color: #87938d; font: 12px monospace; }
|
||||
.learning-paths strong { font-size: 17px; color: var(--ink); }
|
||||
.learning-paths small { font-size: 12px; color: var(--muted); }
|
||||
.learning-paths a:hover strong, .docs-groups a:hover { color: var(--blue); }
|
||||
.doc-search { position: relative; margin: 0; width: 100%; min-width: 0; }
|
||||
.doc-search label { display: block; color: var(--muted); font-size: 12px; margin-bottom: 8px; }
|
||||
.search-field { display: flex; padding: 5px 6px 5px 16px; border: 1px solid #c9d5ce; border-radius: 8px; background: white; align-items: center; }
|
||||
.search-field:focus-within { border-color: var(--blue); box-shadow: 0 0 0 3px #087f5b14; }
|
||||
.search-field input { border: 0; outline: 0; background: transparent; min-width: 0; flex: 1; font-size: 14px; height: 38px; color: var(--ink); }
|
||||
.search-field input:focus-visible { outline: none; }
|
||||
.search-results { position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 35; background: white; border: 1px solid var(--line); border-radius: 8px; max-height: 420px; overflow-y: auto; box-shadow: 0 12px 30px #192e2118; }
|
||||
.search-results[hidden] { display: none; }
|
||||
.search-results a { display: block; text-decoration: none; padding: 13px 16px; border-bottom: 1px solid var(--line); }
|
||||
.search-results a:hover, .search-results a:focus-visible { background: #eef7f1; }
|
||||
.search-results strong { display: block; font-size: 14px; color: var(--ink); }
|
||||
.search-results small { display: block; font-size: 12px; color: var(--muted); margin-top: 4px; overflow-wrap: anywhere; }
|
||||
.search-results p { margin: 0; padding: 18px; font-size: 13px; }
|
||||
.selector { gap: 24px; }
|
||||
.segments { border-radius: 6px; }
|
||||
.segments button { font-size: 13px; min-height: 40px; }
|
||||
.segments button[aria-pressed=true] { background: #1b2922; color: #e6f7ec; }
|
||||
.selector-result { border-radius: 8px; background: #f4f8f5; padding: 24px; margin-top: 20px; }
|
||||
.selector-result h3 { font-size: 20px; }
|
||||
.selector-result p { font-size: 13px; }
|
||||
.limitation { border-left-color: #c5864d; }
|
||||
.matrix-section { padding-top: 30px; }
|
||||
.matrix-section .section-heading { display: none; }
|
||||
.matrix-disclosure summary { cursor: pointer; font-size: 15px; font-weight: 600; padding: 12px 0; }
|
||||
.matrix-disclosure summary span { color: var(--muted); font: 12px monospace; margin-left: 10px; }
|
||||
.table-wrap { width: 100%; max-width: 100%; overflow-x: auto; border-radius: 0; }
|
||||
table { font-size: 13px; }
|
||||
th { background: #f6f8f7; }
|
||||
td, th { padding: 14px; }
|
||||
.terminal, .command-block, .docs-article pre { background: #1c2521; border-color: #35443b; border-radius: 8px; color: #e6eee9; }
|
||||
.terminal-bar { background: #26362d; border-radius: 8px 8px 0 0; }
|
||||
.command-block pre { font-size: 13px; line-height: 1.8; }
|
||||
.api-section { align-items: center; }
|
||||
.responsibility-grid > div, .ecosystem-repositories > article { border-radius: 0; background: transparent; }
|
||||
.responsibility-grid h3, .ecosystem-repositories h3 { font-size: 18px; }
|
||||
.responsibility-grid p, .ecosystem-repositories p { font-size: 14px; }
|
||||
.final-cta h2 { max-width: 620px; font-size: 25px; }
|
||||
.site-footer { padding: 42px max(24px, calc((100% - 1200px) / 2)); background: #17211c; color: #c1d0c7; font-size: 13px; }
|
||||
.site-footer a { color: #ceded4; }
|
||||
.site-footer p { color: #9eb3a6; font-size: 12px; }
|
||||
.page-intro, .detail-hero { max-width: 1200px; padding: 48px 0 36px; margin: 0 auto; background: none; border-bottom: 1px solid var(--line); }
|
||||
.page-intro h1, .detail-heading h1 { font-size: 38px; }
|
||||
.intro-metrics dd { font-size: 24px; }
|
||||
.detail-meta { border-radius: 6px; background: #f4f7f5; }
|
||||
.detail-meta dd { font-size: 13px; }
|
||||
.deploy-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
|
||||
.deployment-card { padding: 24px; border: 1px solid var(--line); border-radius: 8px; box-shadow: none; background: #fff; }
|
||||
.deployment-card h2 { font-size: 22px; }
|
||||
.deployment-card:hover { border-color: #8ab7a0; }
|
||||
.deployment-card p, .deployment-card dl { font-size: 13px; }
|
||||
.card-limitation { border-radius: 0; background: transparent; border-top: 1px solid var(--line); padding: 12px 0; }
|
||||
.content-section { max-width: 1040px; }
|
||||
.content-section h2 { font-size: 26px; }
|
||||
.runtime-path { border-radius: 0; border: 0; border-top: 1px solid var(--line); background: none; padding: 20px 0; }
|
||||
.limitation-callout { border-radius: 0; background: #fbf7ee; padding: 24px; }
|
||||
.limitation-callout h2 { font-size: 20px; }
|
||||
.status { border-radius: 4px; font-size: 11px; }
|
||||
.docs-layout { display: grid; grid-template-columns: 230px minmax(0, 790px) 190px; max-width: 1328px; gap: 36px; padding: 30px 24px 70px; margin: 0 auto; align-items: start; }
|
||||
.docs-sidebar { position: sticky; top: 100px; max-height: calc(100vh - 124px); overflow-y: auto; font-size: 13px; padding-right: 18px; border-right: 1px solid var(--line); }
|
||||
.docs-sidebar a { text-decoration: none; display: block; padding: 7px 10px; margin: 2px 0; border-radius: 5px; color: #5a665f; }
|
||||
.docs-sidebar a:hover { color: var(--blue); background: var(--soft); }
|
||||
.docs-sidebar a[aria-current=page] { color: #086a47; background: #e9f5ed; font-weight: 600; }
|
||||
.docs-home { font-weight: 700; color: var(--ink) !important; }
|
||||
.docs-sidebar details { margin: 22px 0; }
|
||||
.docs-sidebar summary { font-size: 12px; font-weight: 700; padding: 0 10px 6px; cursor: pointer; }
|
||||
.docs-body { min-width: 0; }
|
||||
.breadcrumbs { display: flex; flex-wrap: wrap; gap: 8px; color: var(--muted); font-size: 12px; margin-bottom: 32px; }
|
||||
.breadcrumbs a { text-decoration: none; }
|
||||
.docs-title h1 { font-size: 36px; line-height: 1.4; margin: 0 0 16px; }
|
||||
.docs-title p { color: var(--muted); font-size: 15px; margin-bottom: 26px; }
|
||||
.source-link { font-size: 12px; color: var(--blue); }
|
||||
.source-note { font-size: 12px; color: var(--muted); padding: 12px 0; border-bottom: 1px solid var(--line); }
|
||||
.source-title-anchor { height: 0; }
|
||||
.docs-article { font-size: 15px; line-height: 1.9; margin-top: 34px; min-width: 0; overflow-wrap: anywhere; }
|
||||
.docs-article h2 { font-size: 25px; margin: 42px 0 16px; padding-top: 14px; border-top: 1px solid var(--line); }
|
||||
.docs-article h3 { font-size: 19px; margin: 28px 0 12px; }
|
||||
.docs-article a { color: var(--blue); }
|
||||
.docs-article p { margin: 12px 0; }
|
||||
.docs-article ul, .docs-article ol { padding-left: 24px; }
|
||||
.docs-article li { margin: 6px 0; }
|
||||
.docs-article pre { position: relative; overflow-x: auto; padding: 24px 50px 24px 20px; margin: 20px 0; max-width: 100%; font-size: 13px; line-height: 1.8; }
|
||||
.docs-article pre code { background: none; color: inherit; white-space: pre; padding: 0; }
|
||||
.docs-article :not(pre) > code { padding: 2px 5px; border-radius: 4px; background: #f0f4f1; font-size: .88em; }
|
||||
.docs-article table { display: block; overflow-x: auto; max-width: 100%; width: 100%; }
|
||||
.docs-article blockquote { border-left: 3px solid #7ea98e; background: #f3f8f4; margin: 20px 0; padding: 8px 20px; color: #52655a; }
|
||||
.docs-toc { position: sticky; top: 104px; max-height: calc(100vh - 130px); overflow-y: auto; border-left: 1px solid var(--line); padding-left: 18px; font-size: 12px; }
|
||||
.docs-toc strong { color: var(--ink); }
|
||||
.docs-toc ul { list-style: none; padding-left: 0; }
|
||||
.docs-toc ul ul { padding-left: 10px; }
|
||||
.docs-toc a { color: var(--muted); display: block; text-decoration: none; padding: 5px 0; line-height: 1.5; }
|
||||
.docs-toc a:hover { color: var(--blue); }
|
||||
.docs-groups { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px; margin-top: 36px; }
|
||||
.docs-groups section { border-top: 1px solid var(--line); padding-top: 20px; }
|
||||
.chapter-number { color: #89988e; font: 12px monospace; }
|
||||
.docs-groups h2 { font-size: 20px; margin: 8px 0 12px; }
|
||||
.docs-groups a { display: flex; justify-content: space-between; gap: 16px; padding: 8px 0; font-size: 14px; text-decoration: none; }
|
||||
.docs-next { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 16px; border-top: 1px solid var(--line); padding-top: 24px; margin-top: 48px; font-size: 13px; }
|
||||
.legacy-page { color: var(--ink); background: white; }
|
||||
.legacy-page .site-header a { text-decoration: none; }
|
||||
.legacy-page article { padding: 48px 0 40px; }
|
||||
.legacy-page article > .container, .legacy-page main > .container { max-width: 1040px; }
|
||||
.legacy-page article h1 { font-size: 38px; }
|
||||
.legacy-page article h2 { font-size: 25px; margin-top: 42px; border-top: 1px solid var(--line); }
|
||||
.legacy-page article h3 { font-size: 19px; }
|
||||
.legacy-page article p, .legacy-page article li { font-size: 15px; line-height: 1.9; }
|
||||
.legacy-page .model-card { box-shadow: none; border: 0; border-top: 1px solid var(--line); border-radius: 0; padding: 26px 0; margin: 0; }
|
||||
.legacy-page .model-name { font-size: 23px; }
|
||||
.legacy-page .card, .legacy-page .blog-card { border-radius: 8px; box-shadow: none; }
|
||||
.legacy-page pre { background: #1c2521; color: #e6eee9; border-radius: 8px; font-size: 13px; position: relative; }
|
||||
.legacy-page .highlight, .legacy-page .callout { border-radius: 4px; background: #fbf7ee; }
|
||||
.legacy-page .badge { border-radius: 4px; }
|
||||
.legacy-page .site-footer { margin-top: 30px; text-align: left; }
|
||||
.legacy-page .site-footer p { font-size: 12px; }
|
||||
.legacy-page footer strong { color: white; }
|
||||
.legacy-page .hero, .legacy-page .page-header { height: auto; min-height: 0; padding: 50px 24px; background: #f5f8f6; color: var(--ink); }
|
||||
.legacy-page .hero h1 { color: var(--ink); font-size: 38px; }
|
||||
.legacy-page .hero p { color: var(--muted); }
|
||||
.legacy-page .post-card { min-width: 0; overflow-wrap: anywhere; }
|
||||
.legacy-page .launch-feature { margin-top: 36px; }
|
||||
.legacy-page .launch-feature h2 { font-size: 22px; }
|
||||
[id] { scroll-margin-top: 100px; }
|
||||
@media (max-width: 1280px) {
|
||||
.section, .page-intro, .detail-hero { width: calc(100% - 48px); }
|
||||
.docs-layout { grid-template-columns: 210px minmax(0, 1fr); gap: 28px; }
|
||||
.docs-toc { display: none; }
|
||||
}
|
||||
@media (max-width: 1050px) {
|
||||
.site-header { grid-template-columns: auto 1fr auto auto; padding: 0 24px; height: 64px; min-height: 64px; }
|
||||
.menu-button { display: inline-flex; grid-column: 3; grid-row: 1; }
|
||||
.header-actions { grid-column: 4; grid-row: 1; }
|
||||
.primary-navigation { display: none; position: absolute; top: 64px; left: 0; right: 0; background: white; border-bottom: 1px solid var(--line); padding: 12px 24px; box-shadow: 0 10px 16px #172b2110; }
|
||||
.primary-navigation[data-open=true] { display: grid; grid-template-columns: repeat(4, 1fr); }
|
||||
.primary-navigation a { white-space: normal; }
|
||||
.docs-layout { grid-template-columns: 180px minmax(0, 1fr); gap: 24px; }
|
||||
.repository-router-links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
body { font-size: 15px; }
|
||||
body h1, .page-intro h1, .detail-heading h1, .legacy-page article h1 { font-size: 30px; }
|
||||
body h2, .section-heading h2 { font-size: 24px; }
|
||||
.site-header { padding: 0 16px; }
|
||||
.primary-navigation[data-open=true] { grid-template-columns: repeat(2, 1fr); padding: 12px 16px; }
|
||||
.hero { height: 610px; }
|
||||
.hero-content { width: calc(100% - 40px); padding-top: 36px; }
|
||||
.hero h1 { font-size: 56px; }
|
||||
.hero .hero-copy { font-size: 24px; }
|
||||
.hero-description { font-size: 14px; max-width: 440px; }
|
||||
.hero-actions { gap: 10px; }
|
||||
.hero-actions .button { padding: 10px 13px; font-size: 13px; }
|
||||
.audio-proof { flex-direction: column; align-items: flex-start; gap: 16px; margin-top: 30px; padding-top: 18px; }
|
||||
.audio-proof p { font-size: 13px; }
|
||||
.audio-proof audio { width: min(280px, 100%); }
|
||||
.hero-image { height: 100px; }
|
||||
.section, .page-intro, .detail-hero { width: calc(100% - 40px); padding: 34px 0; }
|
||||
.knowledge-entry { display: block; }
|
||||
.learning-paths { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
|
||||
.learning-paths strong { font-size: 16px; }
|
||||
.repository-router-links { gap: 14px; }
|
||||
.repository-router-links a { min-height: 124px; }
|
||||
.repository-router-links span { font-size: 14px; }
|
||||
.repository-router-links strong { font-size: 12px; }
|
||||
.selector { gap: 18px; }
|
||||
.selector-result { padding: 18px; }
|
||||
.selector-result h3 { font-size: 18px; }
|
||||
.deploy-grid { grid-template-columns: 1fr; }
|
||||
.deployment-card { padding: 20px; }
|
||||
.docs-layout { display: block; padding: 24px 20px 50px; }
|
||||
.docs-sidebar { position: static; max-height: none; border-right: 0; padding: 0 0 16px; margin-bottom: 24px; border-bottom: 1px solid var(--line); }
|
||||
.docs-sidebar details { display: inline-block; vertical-align: top; width: calc(50% - 8px); margin: 6px 4px 6px 0; }
|
||||
.docs-sidebar details:not([open]) nav { display: none; }
|
||||
.docs-sidebar details[open] { width: 100%; }
|
||||
.docs-title h1 { font-size: 29px; }
|
||||
.breadcrumbs { margin-bottom: 20px; }
|
||||
.docs-groups { gap: 24px; grid-template-columns: 1fr; }
|
||||
.docs-article { font-size: 14px; }
|
||||
.docs-article h2 { font-size: 23px; }
|
||||
.docs-article pre { font-size: 12px; }
|
||||
.legacy-page .container { padding: 0 20px; }
|
||||
.legacy-page table { white-space: normal; min-width: 620px; font-size: 12px; }
|
||||
.legacy-page article { padding-top: 30px; }
|
||||
.legacy-page .hero h1 { font-size: 30px; }
|
||||
.site-footer { padding: 36px 20px; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html { scroll-behavior: auto; }
|
||||
*, *::before, *::after { animation: none !important; transition: none !important; }
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,107 @@
|
||||
(() => {
|
||||
const language = document.body.dataset.language || 'zh';
|
||||
const chinese = language === 'zh';
|
||||
let searchData;
|
||||
const getSearchData = () => {
|
||||
if (!searchData) {
|
||||
searchData = fetch(`/search-${language}.json`).then((response) => {
|
||||
if (!response.ok) throw new Error('search_unavailable');
|
||||
return response.json();
|
||||
}).catch((error) => { searchData = null; throw error; });
|
||||
}
|
||||
return searchData;
|
||||
};
|
||||
document.querySelectorAll('[data-doc-search]').forEach((form) => {
|
||||
const input = form.querySelector('input');
|
||||
const results = form.querySelector('.search-results');
|
||||
let timer;
|
||||
const show = async () => {
|
||||
const query = input.value.trim().toLocaleLowerCase();
|
||||
if (!query) { results.hidden = true; results.replaceChildren(); return; }
|
||||
try {
|
||||
const entries = await getSearchData();
|
||||
if (query !== input.value.trim().toLocaleLowerCase()) return;
|
||||
const terms = query.split(/\s+/);
|
||||
const ranked = entries.map((entry) => {
|
||||
const title = entry.title.toLocaleLowerCase();
|
||||
const text = entry.text.toLocaleLowerCase();
|
||||
return { ...entry, score: terms.every((term) => (title + text).includes(term))
|
||||
? terms.reduce((score, term) => score + (title.includes(term) ? 10 : 1), 0) : 0 };
|
||||
}).filter((entry) => entry.score).sort((a, b) => b.score - a.score).slice(0, 10);
|
||||
results.replaceChildren();
|
||||
for (const entry of ranked) {
|
||||
const link = document.createElement('a');
|
||||
link.href = entry.url;
|
||||
const title = document.createElement('strong');
|
||||
title.textContent = entry.title;
|
||||
const snippet = document.createElement('small');
|
||||
const offset = Math.max(0, entry.text.toLocaleLowerCase().indexOf(terms[0]) - 35);
|
||||
snippet.textContent = entry.text.slice(offset, offset + 130);
|
||||
link.append(title, snippet);
|
||||
results.append(link);
|
||||
}
|
||||
if (!ranked.length) {
|
||||
const message = document.createElement('p');
|
||||
message.textContent = chinese ? '没有找到相关文档。试试模型名称或部署方式。' : 'No matching documents. Try a model or runtime name.';
|
||||
results.append(message);
|
||||
}
|
||||
results.hidden = false;
|
||||
} catch {
|
||||
results.replaceChildren();
|
||||
const message = document.createElement('p');
|
||||
message.textContent = chinese ? '搜索暂时不可用,请从文档目录浏览。' : 'Search is unavailable. Browse the documentation index instead.';
|
||||
results.append(message);
|
||||
results.hidden = false;
|
||||
}
|
||||
};
|
||||
input.addEventListener('input', () => { clearTimeout(timer); timer = setTimeout(show, 120); });
|
||||
form.addEventListener('submit', (event) => { event.preventDefault(); clearTimeout(timer); show(); });
|
||||
form.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape') { results.hidden = true; input.focus(); }
|
||||
if (event.key === 'ArrowDown' && event.target === input) { event.preventDefault(); results.querySelector('a')?.focus(); }
|
||||
});
|
||||
document.addEventListener('click', (event) => { if (!form.contains(event.target)) results.hidden = true; });
|
||||
const query = new URLSearchParams(location.search).get('q');
|
||||
if (query) { input.value = query; show(); }
|
||||
});
|
||||
if (matchMedia('(max-width: 720px)').matches) {
|
||||
document.querySelectorAll('.docs-sidebar details').forEach((group) => { group.open = false; });
|
||||
}
|
||||
document.querySelectorAll('.docs-article pre, .legacy-page article pre').forEach((pre) => {
|
||||
if (pre.querySelector('button')) return;
|
||||
const text = pre.textContent.trim();
|
||||
const button = document.createElement('button');
|
||||
button.className = 'icon-button command-copy';
|
||||
button.type = 'button';
|
||||
const original = chinese ? '复制代码' : 'Copy code';
|
||||
button.title = original;
|
||||
button.setAttribute('aria-label', original);
|
||||
const icon = document.createElement('img');
|
||||
icon.src = document.body.dataset.copyIcon;
|
||||
icon.alt = '';
|
||||
button.append(icon);
|
||||
button.addEventListener('click', async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
button.title = chinese ? '已复制' : 'Copied';
|
||||
button.dataset.copied = 'true';
|
||||
} catch {
|
||||
button.title = chinese ? '复制失败,请选择代码' : 'Copy failed; select the code';
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(pre.querySelector('code') || pre);
|
||||
selection.removeAllRanges(); selection.addRange(range);
|
||||
}
|
||||
button.setAttribute('aria-label', button.title);
|
||||
setTimeout(() => { button.title = original; button.setAttribute('aria-label', original); delete button.dataset.copied; }, 2000);
|
||||
});
|
||||
pre.append(button);
|
||||
});
|
||||
const menu = document.querySelector('[data-menu-toggle]');
|
||||
const navigation = document.querySelector('[data-primary-nav]');
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape' && menu?.getAttribute('aria-expanded') === 'true') {
|
||||
menu.setAttribute('aria-expanded', 'false'); navigation.dataset.open = 'false'; menu.focus();
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -64,9 +64,16 @@
|
||||
button.addEventListener('click', async () => {
|
||||
const target = document.querySelector(button.dataset.copyTarget);
|
||||
if (!target) return;
|
||||
await navigator.clipboard.writeText(target.textContent.trim());
|
||||
button.dataset.copied = 'true';
|
||||
window.setTimeout(() => delete button.dataset.copied, 1400);
|
||||
const original = button.title;
|
||||
try {
|
||||
await navigator.clipboard.writeText(target.textContent.trim());
|
||||
button.dataset.copied = 'true';
|
||||
button.title = document.body.dataset.language === 'zh' ? '已复制' : 'Copied';
|
||||
} catch {
|
||||
button.title = document.body.dataset.language === 'zh' ? '复制失败,请选择代码' : 'Copy failed; select the code';
|
||||
}
|
||||
button.setAttribute('aria-label', button.title);
|
||||
window.setTimeout(() => { delete button.dataset.copied; button.title = original; button.setAttribute('aria-label', original); }, 1400);
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -17,6 +17,7 @@ from jinja2 import Environment, FileSystemLoader, StrictUndefined, select_autoes
|
||||
from legacy import normalize_document
|
||||
from registry import load_registry, validate_registry
|
||||
from selector import MATCH_WEIGHTS
|
||||
from documentation import load_catalogue, doc_route, render_source, write_search_indexes
|
||||
|
||||
|
||||
SITE_ROOT = Path(__file__).resolve().parent
|
||||
@@ -139,7 +140,7 @@ def _page_context(
|
||||
'assets': assets,
|
||||
'github_repository_url': GITHUB_REPOSITORY_URL,
|
||||
'github_url': '/go/github',
|
||||
'docs_url': '/go/docs',
|
||||
'docs_url': '/docs/' if language == 'zh' else '/en/docs/',
|
||||
'releases_url': '/go/releases',
|
||||
}
|
||||
|
||||
@@ -377,6 +378,61 @@ def build(output_dir: Path) -> dict[str, Any]:
|
||||
'hreflang': context['peer_canonical'],
|
||||
})
|
||||
|
||||
catalogue = load_catalogue()
|
||||
for language in ('zh', 'en'):
|
||||
prefix = '/en' if language == 'en' else ''
|
||||
peer_prefix = '' if language == 'en' else '/en'
|
||||
for index, entry in enumerate([None, *catalogue['pages']]):
|
||||
route = f'{prefix}/docs/' if entry is None else doc_route(entry['slug'], language)
|
||||
peer = f'{peer_prefix}/docs/' if entry is None else doc_route(entry['slug'], 'en' if language == 'zh' else 'zh')
|
||||
page_title = entry[language] if entry else ('文档中心' if language == 'zh' else 'Documentation')
|
||||
context = _page_context(
|
||||
language=language, route=route, peer_route=peer,
|
||||
title=f'{page_title} - FunASR', description=page_title,
|
||||
date_modified=registry['verified'], navigation=navigation, assets=assets,
|
||||
)
|
||||
context.update({'catalogue': catalogue, 'doc_entry': entry})
|
||||
if entry:
|
||||
context.update(render_source(entry, language, catalogue))
|
||||
context['previous'] = catalogue['pages'][index - 2] if index > 1 else None
|
||||
context['following'] = catalogue['pages'][index] if index < len(catalogue['pages']) else None
|
||||
_render_page(environment, 'docs.html', route_path(stage, route), context)
|
||||
pages.append({'route': route, 'language': language, 'canonical': context['canonical'], 'hreflang': context['peer_canonical']})
|
||||
|
||||
# Reskin old article shells without maintaining another copy of their content.
|
||||
for path in sorted(stage.rglob('*.html')):
|
||||
soup = BeautifulSoup(path.read_text(encoding='utf-8'), 'html.parser')
|
||||
old_nav = soup.select_one('nav[data-product-navigation]')
|
||||
if old_nav is None:
|
||||
continue
|
||||
relative = path.relative_to(stage)
|
||||
language = 'en' if relative.parts[0] == 'en' else 'zh'
|
||||
route = legacy_route(relative)
|
||||
peer = route[3:] if language == 'en' else f'/en{route}'
|
||||
context = _page_context(language=language, route=route, peer_route=peer,
|
||||
title='', description='', date_modified=registry['verified'], navigation=navigation, assets=assets)
|
||||
shell = BeautifulSoup(environment.get_template('base.html').render(**context), 'html.parser')
|
||||
old_nav.replace_with(shell.select_one('.site-header'))
|
||||
soup.body['data-language'] = language
|
||||
soup.body['data-route'] = route
|
||||
soup.body['data-copy-icon'] = assets['lucide/copy.svg']
|
||||
soup.body['class'] = list(soup.body.get('class', [])) + ['legacy-page']
|
||||
for href in (assets['css/site.css'], assets['css/experience.css']):
|
||||
link = soup.new_tag('link', rel='stylesheet', href=href)
|
||||
soup.head.append(link)
|
||||
for src in (assets['js/site.js'], assets['js/experience.js']):
|
||||
script = soup.new_tag('script', src=src, defer=True)
|
||||
soup.body.append(script)
|
||||
footer = soup.find('footer')
|
||||
if footer:
|
||||
footer.replace_with(shell.select_one('.site-footer'))
|
||||
# Article content and ids are untouched; tables gain a scroll container.
|
||||
for table in soup.select('article table'):
|
||||
wrapper = soup.new_tag('div', attrs={'class': 'table-wrap'})
|
||||
table.wrap(wrapper)
|
||||
path.write_text(str(soup), encoding='utf-8')
|
||||
|
||||
write_search_indexes(stage)
|
||||
last_modified_by_route = {
|
||||
entry['routes'][language]: entry['tested']['verified']
|
||||
for entry in registry['deployments']
|
||||
|
||||
@@ -216,6 +216,8 @@
|
||||
},
|
||||
"evidence": [
|
||||
{"label": "H100 native FunASR validation record", "url": "https://github.com/modelscope/FunASR/blob/main/docs/vllm_native_funasr_validation.md"},
|
||||
{"label": "official checkpoint registered upstream (merged PR #54944)", "url": "https://github.com/vllm-project/vllm/pull/54944"},
|
||||
{"label": "official checkpoint at a4362c94 (separate from historical tests below)", "url": "https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512-vllm/tree/a4362c943d48951f98ca2a62181cc028970270c5"},
|
||||
{"label": "vLLM 0.27.1 release and CUDA 12.9 wheel", "url": "https://github.com/vllm-project/vllm/releases/tag/v0.27.1"},
|
||||
{"label": "native FunASR model integration", "url": "https://github.com/vllm-project/vllm/pull/33247"},
|
||||
{"label": "native FunASR hotword support", "url": "https://github.com/vllm-project/vllm/pull/39674"},
|
||||
@@ -244,9 +246,9 @@
|
||||
"name": "vLLM 原生 FunASR 服务",
|
||||
"summary": "通过 vLLM 内置的 FunASR 架构,在 NVIDIA GPU 上提供兼容 OpenAI 的多语言转写与热词接口。",
|
||||
"fit": ["需要标准 /v1/audio/transcriptions 接口", "Fun-ASR-Nano 批量或并发转写", "已有 NVIDIA GPU 和 Linux 服务环境"],
|
||||
"not_fit": ["只接受官方发布权重、不能使用社区转换 checkpoint", "纯 CPU 或边缘设备", "尚未完成显存、并发与业务音频压测的公网服务"],
|
||||
"not_fit": ["未经重新验证就直接替换下方固定版本 checkpoint", "纯 CPU 或边缘设备", "尚未完成显存、并发与业务音频压测的公网服务"],
|
||||
"selection_reason": "vLLM 已原生注册 FunASR 架构,并提供 OpenAI 兼容转写、显存调度、并发和热词参数。",
|
||||
"primary_limitation": "当前原生 vLLM 路径依赖社区转换 checkpoint;要求官方权重链路时使用官方 FunASR split-engine。实测仅覆盖单张 H100,必须按目标 GPU、音频、语言、热词与流量复测。",
|
||||
"primary_limitation": "下方历史实测与命令使用社区转换 checkpoint。官方 FunAudioLLM/Fun-ASR-Nano-2512-vllm 已被 vLLM 上游登记(PR #54944);这不等于本页旧测试已覆盖新权重,切换时须重新验证。历史数据仅覆盖单张 H100,目标 GPU、音频、语言、热词与流量均须复测。",
|
||||
"status_label": "社区验证",
|
||||
"operations": ["固定 vLLM wheel、Torch、CUDA、模型 revision 与音频依赖", "等待 /health 就绪并完成预热后再采集容量数据", "记录队列等待、最终结果延迟、显存和热词命中率"],
|
||||
"security": ["在 API 网关实现认证、TLS、限流和音频大小/时长限制", "worker 仅绑定内网地址,不直接暴露公网", "校验 wheel、模型 revision 和示例音频摘要,隔离模型缓存与上传临时目录"],
|
||||
@@ -256,9 +258,9 @@
|
||||
"name": "Native FunASR on vLLM",
|
||||
"summary": "Use vLLM's built-in FunASR architecture for multilingual and hotword-aware transcription through an OpenAI-compatible API on NVIDIA GPUs.",
|
||||
"fit": ["A standard /v1/audio/transcriptions endpoint", "Concurrent or batch Fun-ASR-Nano transcription", "An NVIDIA GPU Linux serving environment"],
|
||||
"not_fit": ["Deployments that accept only officially published weights and cannot use a community conversion", "CPU-only or edge devices", "Internet-facing service before memory, concurrency, and workload tests"],
|
||||
"not_fit": ["Replacing the pinned checkpoint below without revalidation", "CPU-only or edge devices", "Internet-facing service before memory, concurrency, and workload tests"],
|
||||
"selection_reason": "vLLM natively registers the FunASR architecture and provides OpenAI-compatible transcription, memory scheduling, concurrency, and hotword parameters.",
|
||||
"primary_limitation": "The native vLLM path currently depends on a community-converted checkpoint; use the official FunASR split-engine when an official-weight chain is required. Evidence covers one H100 only, so retest the target GPU, audio, languages, hotwords, and traffic.",
|
||||
"primary_limitation": "The historical tests and commands below use a community-converted checkpoint. The official FunAudioLLM/Fun-ASR-Nano-2512-vllm checkpoint is now registered upstream in vLLM PR #54944; this does not extend the old test evidence to new weights. Revalidate any switch and the target GPU, audio, languages, hotwords, and traffic; historical measurements cover one H100 only.",
|
||||
"status_label": "Community verified",
|
||||
"operations": ["Pin the vLLM wheel, Torch, CUDA, model revision, and audio dependencies", "Wait for /health and warm the model before measuring capacity", "Measure queue time, final-result latency, memory, and hotword hit rate"],
|
||||
"security": ["Put authentication, TLS, rate limits, and audio size/duration limits at the gateway", "Bind workers to a private address instead of exposing them directly", "Verify wheel, model revision, and sample digests; isolate model cache and temporary uploads"],
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"groups": [
|
||||
{"id": "start", "zh": "开始使用", "en": "Get started"},
|
||||
{"id": "models", "zh": "模型与能力", "en": "Models & capabilities"},
|
||||
{"id": "deploy", "zh": "部署与接口", "en": "Deploy & integrate"},
|
||||
{"id": "operate", "zh": "评测与运维", "en": "Evaluate & operate"},
|
||||
{"id": "ecosystem", "zh": "应用与生态", "en": "Applications & ecosystem"}
|
||||
],
|
||||
"pages": [
|
||||
{"slug": "model-selection", "group": "start", "zh": "选择第一个模型", "en": "Choose your first model", "source_zh": "docs/model_selection_zh.md", "source_en": "docs/model_selection.md"},
|
||||
{"slug": "command-line", "group": "start", "zh": "命令行转写", "en": "Command-line transcription", "source_zh": "docs/cli.md", "source_en": "docs/cli.md"},
|
||||
{"slug": "migration", "group": "start", "zh": "从 Whisper 与云 API 迁移", "en": "Migrate from Whisper & cloud APIs", "source_zh": "docs/migration_from_whisper_zh.md", "source_en": "docs/migration_from_whisper.md"},
|
||||
{"slug": "moss-transcribe-diarize", "group": "models", "zh": "MOSS 转写与说话人分离", "en": "MOSS transcription & diarization", "source_zh": "docs/moss_transcribe_diarize_zh.md", "source_en": "docs/moss_transcribe_diarize.md"},
|
||||
{"slug": "deployment-matrix", "group": "deploy", "zh": "运行时与部署矩阵", "en": "Runtime & deployment matrix", "source_zh": "docs/deployment_matrix_zh.md", "source_en": "docs/deployment_matrix.md"},
|
||||
{"slug": "vllm", "group": "deploy", "zh": "vLLM split-engine 推理", "en": "vLLM split-engine inference", "source_zh": "docs/vllm_guide_zh.md", "source_en": "docs/vllm_guide.md"},
|
||||
{"slug": "native-vllm", "group": "deploy", "zh": "原生 vLLM 验证记录", "en": "Native vLLM validation record", "source_zh": "docs/vllm_native_funasr_validation.md", "source_en": "docs/vllm_native_funasr_validation.md"},
|
||||
{"slug": "troubleshooting", "group": "operate", "zh": "安装与推理故障排查", "en": "Troubleshooting", "source_zh": "docs/troubleshooting_zh.md", "source_en": "docs/troubleshooting.md"},
|
||||
{"slug": "benchmark-method", "group": "operate", "zh": "可复现性能评测", "en": "Reproducible performance tests", "source_zh": "docs/benchmark/rtf_reproducibility.md", "source_en": "docs/benchmark/rtf_reproducibility.md"},
|
||||
{"slug": "realtime-benchmark", "group": "operate", "zh": "实时 WebSocket 压测", "en": "Realtime WebSocket benchmarking", "source_zh": "docs/benchmark/realtime_ws_benchmark.md", "source_en": "docs/benchmark/realtime_ws_benchmark.md"},
|
||||
{"slug": "use-cases", "group": "ecosystem", "zh": "应用场景与示例", "en": "Use cases & examples", "source_zh": "docs/use_case_showcase_zh.md", "source_en": "docs/use_case_showcase.md"},
|
||||
{"slug": "community", "group": "ecosystem", "zh": "社区集成", "en": "Community integrations", "source_zh": "docs/community_projects_zh.md", "source_en": "docs/community_projects.md"},
|
||||
{"slug": "repositories", "group": "ecosystem", "zh": "四仓职责与贡献入口", "en": "Repositories & contribution paths", "source_zh": "docs/repository_roles_zh.md", "source_en": "docs/repository_roles.md"}
|
||||
]
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
{"id": "benchmarks", "zh": "性能", "en": "Benchmarks", "href_zh": "/benchmarks.html", "href_en": "/en/benchmarks.html"},
|
||||
{"id": "ecosystem", "zh": "生态", "en": "Ecosystem", "href_zh": "/ecosystem.html", "href_en": "/en/ecosystem.html"},
|
||||
{"id": "blog", "zh": "博客", "en": "Blog", "href_zh": "/blog/", "href_en": "/en/blog/"},
|
||||
{"id": "docs", "zh": "文档", "en": "Docs", "href_zh": "https://github.com/modelscope/FunASR/tree/main/docs", "href_en": "https://github.com/modelscope/FunASR/tree/main/docs"},
|
||||
{"id": "docs", "zh": "文档", "en": "Docs", "href_zh": "/docs/", "href_en": "/en/docs/"},
|
||||
{"id": "donors", "zh": "功德榜", "en": "Donors", "href_zh": "/donors.html", "href_en": "/en/donors.html"}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
"""Render the documentation catalogue from repository-owned Markdown sources."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import posixpath
|
||||
import re
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote, unquote, urlsplit, urlunsplit
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from markdown import Markdown
|
||||
|
||||
SITE_ROOT = Path(__file__).resolve().parent
|
||||
REPO_ROOT = SITE_ROOT.parents[1]
|
||||
GITHUB = 'https://github.com/modelscope/FunASR/blob/main/'
|
||||
RAW = 'https://raw.githubusercontent.com/modelscope/FunASR/main/'
|
||||
|
||||
|
||||
def source_slug(value: str, separator: str) -> str:
|
||||
"""Preserve Unicode and repeated spaces used by source Markdown fragments."""
|
||||
return re.sub(r'[^\w\- ]', '', value.lower()).replace(' ', separator)
|
||||
|
||||
|
||||
def load_catalogue() -> dict:
|
||||
catalogue = json.loads((SITE_ROOT / 'data/documentation.json').read_text())
|
||||
for entry in catalogue['pages']:
|
||||
for language in ('zh', 'en'):
|
||||
path = (REPO_ROOT / entry[f'source_{language}']).resolve()
|
||||
if not path.is_relative_to(REPO_ROOT) or not path.is_file():
|
||||
raise ValueError(f'Missing documentation source: {path}')
|
||||
return catalogue
|
||||
|
||||
|
||||
def doc_route(slug: str, language: str) -> str:
|
||||
return f'/{"en/" if language == "en" else ""}docs/{slug}.html'
|
||||
|
||||
|
||||
def render_source(entry: dict, language: str, catalogue: dict) -> dict:
|
||||
relative = entry[f'source_{language}']
|
||||
parser = Markdown(extensions=['extra', 'toc', 'sane_lists'],
|
||||
extension_configs={'toc': {'slugify': source_slug}})
|
||||
soup = BeautifulSoup(parser.convert((REPO_ROOT / relative).read_text()), 'html.parser')
|
||||
local_routes = {
|
||||
page[f'source_{language}']: doc_route(page['slug'], language)
|
||||
for page in catalogue['pages']
|
||||
}
|
||||
for element, attr in [('a', 'href'), ('img', 'src')]:
|
||||
for node in soup.select(f'{element}[{attr}]'):
|
||||
value = str(node[attr])
|
||||
parsed = urlsplit(value)
|
||||
if parsed.scheme or parsed.netloc or not parsed.path or parsed.path.startswith('/'):
|
||||
continue
|
||||
resolved = posixpath.normpath(posixpath.join(posixpath.dirname(relative), unquote(parsed.path)))
|
||||
if resolved.startswith('../'):
|
||||
raise ValueError(f'Documentation link escapes repository: {relative}: {value}')
|
||||
destination = local_routes.get(resolved) if element == 'a' else None
|
||||
if destination is None:
|
||||
destination = (RAW if element == 'img' else GITHUB) + quote(resolved, safe='/')
|
||||
node[attr] = urlunsplit((*urlsplit(destination)[:3], parsed.query, parsed.fragment))
|
||||
heading = soup.find('h1')
|
||||
if heading:
|
||||
# Keep source anchor names when the document title is moved into the shell.
|
||||
heading.name = 'div'
|
||||
heading['class'] = ['source-title-anchor']
|
||||
heading.clear()
|
||||
for index, code in enumerate(soup.select('pre')):
|
||||
code['id'] = code.get('id', f'code-{index}')
|
||||
return {
|
||||
'content_html': str(soup),
|
||||
'toc_html': parser.toc,
|
||||
'source_url': GITHUB + quote(relative, safe='/'),
|
||||
'source_english_only': language == 'zh' and relative == entry['source_en'],
|
||||
}
|
||||
|
||||
|
||||
def write_search_indexes(stage: Path) -> None:
|
||||
indexes: dict[str, list] = {'zh': [], 'en': []}
|
||||
for path in sorted(stage.rglob('*.html')):
|
||||
if path.name == '404.html':
|
||||
continue
|
||||
soup = BeautifulSoup(path.read_text(), 'html.parser')
|
||||
heading = soup.select_one('h1')
|
||||
canonical = soup.select_one('link[rel="canonical"]')
|
||||
if heading is None or canonical is None:
|
||||
continue
|
||||
url = urlsplit(canonical['href']).path
|
||||
if url in {'/', '/en/'} or '/static/' in url or '/voice/' in url:
|
||||
continue
|
||||
content = soup.select_one('.docs-article') or soup.select_one('main') or soup.select_one('article') or soup.body
|
||||
if content is None:
|
||||
continue
|
||||
for node in content.select('script, style, nav, .site-header, .site-footer'):
|
||||
node.decompose()
|
||||
indexes['en' if url.startswith('/en/') else 'zh'].append({
|
||||
'title': heading.get_text(' ', strip=True),
|
||||
'url': url,
|
||||
'text': content.get_text(' ', strip=True)[:24000],
|
||||
})
|
||||
for language, entries in indexes.items():
|
||||
(stage / f'search-{language}.json').write_text(json.dumps(entries, ensure_ascii=False))
|
||||
@@ -1,3 +1,4 @@
|
||||
Jinja2==3.1.6
|
||||
beautifulsoup4==4.13.4
|
||||
pytest==8.4.1
|
||||
Markdown==3.8.2
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<meta property="og:description" content="{{ description }}">
|
||||
<meta property="og:url" content="{{ canonical }}">
|
||||
<link rel="stylesheet" href="{{ assets['css/site.css'] }}">
|
||||
<link rel="stylesheet" href="{{ assets['css/experience.css'] }}">
|
||||
<script type="application/ld+json">{{ {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'SoftwareApplication',
|
||||
@@ -26,7 +27,7 @@
|
||||
'license': 'https://github.com/modelscope/FunASR/blob/main/LICENSE'
|
||||
} | tojson }}</script>
|
||||
</head>
|
||||
<body data-language="{{ language }}" data-route="{{ route }}">
|
||||
<body data-language="{{ language }}" data-route="{{ route }}" data-copy-icon="{{ assets['lucide/copy.svg'] }}">
|
||||
<a class="skip-link" href="#main">{{ '跳到主要内容' if language == 'zh' else 'Skip to main content' }}</a>
|
||||
<header class="site-header">
|
||||
<a class="brand" href="{{ '/' if language == 'zh' else '/en/' }}" aria-label="FunASR {{ '首页' if language == 'zh' else 'home' }}">
|
||||
@@ -75,5 +76,6 @@
|
||||
</div>
|
||||
</footer>
|
||||
<script src="{{ assets['js/site.js'] }}" defer></script>
|
||||
<script src="{{ assets['js/experience.js'] }}" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="docs-layout">
|
||||
<aside class="docs-sidebar">
|
||||
<a class="docs-home" href="{{ '/docs/' if language == 'zh' else '/en/docs/' }}">{{ '文档中心' if language == 'zh' else 'Documentation' }}</a>
|
||||
{% for group in catalogue.groups %}
|
||||
<details open>
|
||||
<summary>{{ group[language] }}</summary>
|
||||
<nav aria-label="{{ group[language] }}">
|
||||
{% for page in catalogue.pages if page.group == group.id %}
|
||||
<a href="{{ ('/docs/' if language == 'zh' else '/en/docs/') ~ page.slug ~ '.html' }}"{% if doc_entry and doc_entry.slug == page.slug %} aria-current="page"{% endif %}>{{ page[language] }}</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
</details>
|
||||
{% endfor %}
|
||||
<a href="{{ '/deploy/' if language == 'zh' else '/en/deploy/' }}">{{ '部署手册' if language == 'zh' else 'Deployment guides' }} →</a>
|
||||
</aside>
|
||||
<div class="docs-body">
|
||||
<nav class="breadcrumbs" aria-label="{{ '面包屑' if language == 'zh' else 'Breadcrumbs' }}"><a href="{{ '/' if language == 'zh' else '/en/' }}">FunASR</a><span>/</span><a href="{{ '/docs/' if language == 'zh' else '/en/docs/' }}">{{ '文档' if language == 'zh' else 'Docs' }}</a>{% if doc_entry %}<span>/</span><span>{{ doc_entry[language] }}</span>{% endif %}</nav>
|
||||
{% if doc_entry %}
|
||||
<header class="docs-title"><h1>{{ doc_entry[language] }}</h1><a class="source-link" data-source-link href="{{ source_url }}">{{ '查看 / 编辑源文档' if language == 'zh' else 'View / edit source' }} ↗</a></header>
|
||||
{% if source_english_only %}<p class="source-note">{{ '本篇目前使用英文源文档;导航保留中文。' }}</p>{% endif %}
|
||||
<article class="docs-article">{{ content_html | safe }}</article>
|
||||
<nav class="docs-next" aria-label="{{ '相邻文档' if language == 'zh' else 'Adjacent documents' }}">
|
||||
{% if previous %}<a href="{{ ('/docs/' if language == 'zh' else '/en/docs/') ~ previous.slug ~ '.html' }}">← {{ previous[language] }}</a>{% endif %}
|
||||
{% if following %}<a href="{{ ('/docs/' if language == 'zh' else '/en/docs/') ~ following.slug ~ '.html' }}">{{ following[language] }} →</a>{% endif %}
|
||||
</nav>
|
||||
{% else %}
|
||||
<header class="docs-title"><p class="eyebrow">FUNASR DOCUMENTATION</p><h1>{{ '从第一次转写,到生产部署。' if language == 'zh' else 'From first transcript to production.' }}</h1><p>{{ '模型能力、可运行示例和部署边界,在同一个地方。' if language == 'zh' else 'Model capabilities, runnable examples, and deployment boundaries in one place.' }}</p></header>
|
||||
{% include 'search.html' %}
|
||||
<div class="docs-groups">
|
||||
{% for group in catalogue.groups %}
|
||||
<section><span class="chapter-number">0{{ loop.index }}</span><h2>{{ group[language] }}</h2>
|
||||
{% for page in catalogue.pages if page.group == group.id %}<a href="{{ ('/docs/' if language == 'zh' else '/en/docs/') ~ page.slug ~ '.html' }}">{{ page[language] }} <span aria-hidden="true">→</span></a>{% endfor %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if doc_entry %}<aside class="docs-toc"><strong>{{ '本页目录' if language == 'zh' else 'On this page' }}</strong>{{ toc_html | safe }}</aside>{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,37 +1,32 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<section class="hero" data-section="hero">
|
||||
<img class="hero-image" src="{{ assets['images/deployment-topology.png'] }}" alt="{{ '连接 GPU、CPU、边缘设备和 API 客户端的私有部署拓扑' if language == 'zh' else 'Private deployment topology connecting GPU, CPU, edge devices, and API clients' }}">
|
||||
<div class="hero-shade" aria-hidden="true"></div>
|
||||
<img class="hero-image" src="{{ assets['images/speech-waveform.png'] }}" alt="{{ '公开中文语音样本的实际波形' if language == 'zh' else 'Waveform of the public Chinese speech sample' }}">
|
||||
<div class="hero-content">
|
||||
<p class="eyebrow">{{ '开源语音基础设施' if language == 'zh' else 'Open speech infrastructure' }}</p>
|
||||
<p class="eyebrow">OPEN SOURCE / SPEECH INTELLIGENCE</p>
|
||||
<h1>FunASR</h1>
|
||||
<p class="hero-copy">
|
||||
{{ '可私有化部署的语音智能基础设施。覆盖 GPU 高吞吐、实时流式服务、OpenAI 兼容 API,以及 CPU 与边缘独立运行。' if language == 'zh' else 'Private-deployment speech infrastructure for high-throughput GPU inference, realtime streaming, OpenAI-compatible APIs, and standalone CPU or edge use.' }}
|
||||
{{ '让语音,成为可用的信息。' if language == 'zh' else 'Turn speech into useful information.' }}
|
||||
</p>
|
||||
<p class="hero-description">{{ '语音识别、时间戳、说话人分离。从一行 Python,到你自己的生产服务。' if language == 'zh' else 'Recognition, timestamps, and diarization. From a line of Python to your own production service.' }}</p>
|
||||
<div class="hero-actions">
|
||||
<a class="button button-primary" href="#deployment-selector">
|
||||
{{ '选择部署方案' if language == 'zh' else 'Choose a deployment' }}
|
||||
<a class="button button-primary" href="{{ '/quickstart.html' if language == 'zh' else '/en/quickstart.html' }}">
|
||||
{{ '开始构建' if language == 'zh' else 'Start building' }}
|
||||
<img src="{{ assets['lucide/arrow-right.svg'] }}" alt="">
|
||||
</a>
|
||||
<a class="button button-secondary" href="{{ '/quickstart.html' if language == 'zh' else '/en/quickstart.html' }}">
|
||||
<a class="button button-secondary" href="#deployment-selector">
|
||||
<img src="{{ assets['lucide/terminal.svg'] }}" alt="">
|
||||
{{ '5 分钟验证' if language == 'zh' else 'Verify in five minutes' }}
|
||||
{{ '选择部署方案' if language == 'zh' else 'Choose a deployment' }}
|
||||
</a>
|
||||
</div>
|
||||
<dl class="hero-proof">
|
||||
<div><dt>{{ '许可' if language == 'zh' else 'License' }}</dt><dd>Apache-2.0</dd></div>
|
||||
<div><dt>{{ '接口' if language == 'zh' else 'API' }}</dt><dd>OpenAI compatible</dd></div>
|
||||
<div><dt>{{ '验证日期' if language == 'zh' else 'Verified' }}</dt><dd>{{ verified }}</dd></div>
|
||||
</dl>
|
||||
<div class="audio-proof"><div><span class="sample-label">{{ '公开样本 · 中文' if language == 'zh' else 'PUBLIC SAMPLE · CHINESE' }}</span><p>欢迎大家来体验达摩院推出的语音识别模型。</p></div><audio controls preload="metadata" src="{{ assets['audio/speech-sample.wav'] }}" aria-label="{{ '播放公开中文样本' if language == 'zh' else 'Play public Chinese sample' }}"></audio></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section repository-router" data-section="repository-router">
|
||||
<div class="section-heading compact-heading">
|
||||
<p class="eyebrow">{{ '生态路由' if language == 'zh' else 'Ecosystem routing' }}</p>
|
||||
<h2>{{ '按工作流进入专用仓库' if language == 'zh' else 'Choose the focused repository' }}</h2>
|
||||
<p>{{ '部署路径适用于 FunASR 运行时;当需求由模型能力或视频工作流定义时,直接进入对应仓库。' if language == 'zh' else 'Deployment paths serve the FunASR runtime. When the requirement is defined by model capability or a video workflow, start in the focused repository.' }}</p>
|
||||
<p class="eyebrow">{{ '一个工具箱,多种可能' if language == 'zh' else 'ONE TOOLKIT. MANY POSSIBILITIES.' }}</p>
|
||||
<h2>{{ '为你的应用,选对模型。' if language == 'zh' else 'The right model for your application.' }}</h2>
|
||||
</div>
|
||||
<div class="repository-router-links">
|
||||
<a href="/go/fun-asr">
|
||||
@@ -49,14 +44,30 @@
|
||||
<strong>{{ '从语音定位到视频剪辑' if language == 'zh' else 'From speech localization to video clipping' }}</strong>
|
||||
<img src="{{ assets['lucide/arrow-right.svg'] }}" alt="">
|
||||
</a>
|
||||
<a href="{{ '/docs/moss-transcribe-diarize.html' if language == 'zh' else '/en/docs/moss-transcribe-diarize.html' }}">
|
||||
<span>MOSS <small>OpenMOSS</small></span>
|
||||
<strong>{{ '长音频转写与匿名说话人' if language == 'zh' else 'Long-form transcription & anonymous speakers' }}</strong>
|
||||
<img src="{{ assets['lucide/arrow-right.svg'] }}" alt="">
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section knowledge-entry" data-section="documentation">
|
||||
<div class="section-heading compact-heading"><p class="eyebrow">{{ '开发者文档' if language == 'zh' else 'DEVELOPER DOCUMENTATION' }}</p><h2>{{ '从问题,直接到答案。' if language == 'zh' else 'Find your next step.' }}</h2></div>
|
||||
{% include 'search.html' %}
|
||||
<div class="learning-paths">
|
||||
<a href="{{ '/docs/model-selection.html' if language == 'zh' else '/en/docs/model-selection.html' }}"><span>01</span><strong>{{ '选模型' if language == 'zh' else 'Choose a model' }}</strong><small>{{ '能力、语种与时间戳' if language == 'zh' else 'Capabilities, languages & timestamps' }}</small></a>
|
||||
<a href="{{ '/quickstart.html' if language == 'zh' else '/en/quickstart.html' }}"><span>02</span><strong>{{ '跑通推理' if language == 'zh' else 'Run inference' }}</strong><small>Python / CLI / API</small></a>
|
||||
<a href="{{ '/deploy/' if language == 'zh' else '/en/deploy/' }}"><span>03</span><strong>{{ '部署服务' if language == 'zh' else 'Deploy a service' }}</strong><small>vLLM / llama.cpp / Docker</small></a>
|
||||
<a href="{{ '/docs/troubleshooting.html' if language == 'zh' else '/en/docs/troubleshooting.html' }}"><span>04</span><strong>{{ '稳定运行' if language == 'zh' else 'Operate reliably' }}</strong><small>{{ '评测、监控与故障排查' if language == 'zh' else 'Evaluation, monitoring & troubleshooting' }}</small></a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="deployment-selector" class="section selector-section" data-section="deployment-selector">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">{{ '部署选择器' if language == 'zh' else 'Deployment selector' }}</p>
|
||||
<h2>{{ '从工作负载开始,不从框架名开始' if language == 'zh' else 'Start with the workload, not a framework name' }}</h2>
|
||||
<p>{{ '选择业务形态、硬件和首要目标,得到一条有明确限制的推荐路径。' if language == 'zh' else 'Choose the workload, hardware, and primary goal to get one recommendation with an explicit limitation.' }}</p>
|
||||
<h2>{{ '在你的硬件上,找到合适的运行时。' if language == 'zh' else 'A runtime that fits your hardware.' }}</h2>
|
||||
<p>{{ '从本地 CPU 到 GPU 集群,每条路径都有安装步骤、测试记录和已知限制。' if language == 'zh' else 'From a local CPU to GPU infrastructure, with setup steps, test records, and known limitations.' }}</p>
|
||||
</div>
|
||||
<form class="selector" data-deployment-selector>
|
||||
<fieldset>
|
||||
@@ -108,8 +119,9 @@
|
||||
<section class="section matrix-section" data-section="deployment-matrix">
|
||||
<div class="section-heading compact-heading">
|
||||
<p class="eyebrow">{{ '完整矩阵' if language == 'zh' else 'Full matrix' }}</p>
|
||||
<h2>{{ '无 JavaScript 也能完成选择' if language == 'zh' else 'The full decision path works without JavaScript' }}</h2>
|
||||
<h2>{{ '运行时一览' if language == 'zh' else 'Explore the runtimes' }}</h2>
|
||||
</div>
|
||||
<details class="matrix-disclosure"><summary>{{ '查看全部部署路径' if language == 'zh' else 'Compare all deployment paths' }} <span>{{ deployments | length }}</span></summary>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr>
|
||||
@@ -132,6 +144,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="section api-section" data-section="api-contract">
|
||||
@@ -142,13 +155,13 @@
|
||||
</div>
|
||||
<div class="terminal" aria-label="{{ 'OpenAI 兼容 API 验证命令' if language == 'zh' else 'OpenAI-compatible API verification commands' }}">
|
||||
<div class="terminal-bar"><span></span><span></span><span></span><strong>localhost:8000</strong></div>
|
||||
<pre><code><span class="prompt">$</span> curl -fsS http://localhost:8000/health
|
||||
{"status":"ok"}
|
||||
<pre><code>curl -fsS http://localhost:8000/health
|
||||
# {"status":"ok"}
|
||||
|
||||
<span class="prompt">$</span> curl -fsS http://localhost:8000/v1/models
|
||||
{"data":[{"id":"sensevoice"}]}
|
||||
curl -fsS http://localhost:8000/v1/models
|
||||
# {"data":[{"id":"sensevoice"}]}
|
||||
|
||||
<span class="prompt">$</span> curl http://localhost:8000/v1/audio/transcriptions \
|
||||
curl -fsS http://localhost:8000/v1/audio/transcriptions \
|
||||
-F [email protected] -F model=sensevoice</code></pre>
|
||||
<button class="icon-button copy-button" type="button" data-copy-target=".terminal pre" title="{{ '复制命令' if language == 'zh' else 'Copy commands' }}">
|
||||
<img src="{{ assets['lucide/copy.svg'] }}" alt="">
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<form class="doc-search" data-doc-search role="search" action="{{ '/docs/' if language == 'zh' else '/en/docs/' }}">
|
||||
<label for="docs-query">{{ '搜索文档' if language == 'zh' else 'Search documentation' }}</label>
|
||||
<div class="search-field">
|
||||
<input id="docs-query" name="q" type="search" autocomplete="off" placeholder="{{ '模型、部署、时间戳、MOSS…' if language == 'zh' else 'Models, deployment, timestamps, MOSS…' }}" aria-controls="search-results">
|
||||
<button class="icon-button" type="submit" title="{{ '搜索' if language == 'zh' else 'Search' }}"><img src="{{ assets['lucide/arrow-right.svg'] }}" alt=""></button>
|
||||
</div>
|
||||
<div id="search-results" class="search-results" hidden aria-live="polite"></div>
|
||||
</form>
|
||||
@@ -0,0 +1,55 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
for (const width of [320, 390, 768, 1440, 1920]) {
|
||||
test(`documentation and legacy layouts fit ${width}px`, async ({ page }) => {
|
||||
await page.setViewportSize({ width, height: 900 });
|
||||
const errors: string[] = [];
|
||||
page.on('pageerror', error => errors.push(error.message));
|
||||
for (const route of ['/docs/', '/en/docs/', '/docs/moss-transcribe-diarize.html', '/models.html', '/blog/', '/donors.html']) {
|
||||
await page.goto(route);
|
||||
await expect(page.locator('[data-primary-nav]')).toHaveCount(1);
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth - innerWidth)).toBeLessThanOrEqual(1);
|
||||
}
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
}
|
||||
|
||||
test('search is bilingual, keyboard accessible, and handles missing results', async ({ page }) => {
|
||||
for (const prefix of ['', '/en']) {
|
||||
await page.goto(`${prefix}/docs/`);
|
||||
const query = page.locator('[data-doc-search] input');
|
||||
await query.fill('MOSS');
|
||||
const first = page.locator('.search-results a').first();
|
||||
await expect(first).toBeVisible();
|
||||
await query.press('ArrowDown');
|
||||
await expect(first).toBeFocused();
|
||||
await expect(first).toHaveAttribute('href', new RegExp(`^${prefix}/`));
|
||||
await query.fill('zzzz-no-document-match-48291');
|
||||
await expect(page.locator('.search-results')).toContainText(prefix ? 'No matching documents' : '没有找到');
|
||||
}
|
||||
});
|
||||
|
||||
test('source-derived code copies cleanly and legacy menu closes with Escape', async ({ page, context }) => {
|
||||
await context.grantPermissions(['clipboard-read', 'clipboard-write']);
|
||||
await page.goto('/docs/moss-transcribe-diarize.html');
|
||||
const block = page.locator('.docs-article pre').first();
|
||||
const expected = await block.locator('code').innerText();
|
||||
await block.locator('button').click();
|
||||
expect((await page.evaluate(() => navigator.clipboard.readText())).trim()).toBe(expected.trim());
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.goto('/models.html');
|
||||
const menu = page.locator('[data-menu-toggle]');
|
||||
await menu.press('Enter');
|
||||
await expect(menu).toHaveAttribute('aria-expanded', 'true');
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(menu).toHaveAttribute('aria-expanded', 'false');
|
||||
});
|
||||
|
||||
test('public speech sample is playable and waveform has actual pixels', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect.poll(() => page.locator('audio').evaluate((audio: HTMLAudioElement) => audio.duration)).toBeGreaterThan(5);
|
||||
await page.locator('audio').evaluate((audio: HTMLAudioElement) => audio.play());
|
||||
await expect.poll(() => page.locator('audio').evaluate((audio: HTMLAudioElement) => audio.currentTime)).toBeGreaterThan(0);
|
||||
await page.locator('audio').evaluate((audio: HTMLAudioElement) => audio.pause());
|
||||
expect(await page.locator('.hero-image').evaluate((image: HTMLImageElement) => image.naturalWidth)).toBeGreaterThan(1000);
|
||||
});
|
||||
@@ -6,7 +6,7 @@ const python = process.env.SITE_PYTHON || 'python3';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: '.',
|
||||
testMatch: 'product-site.spec.ts',
|
||||
testMatch: '*.spec.ts',
|
||||
timeout: 45_000,
|
||||
workers: 1,
|
||||
reporter: 'line',
|
||||
|
||||
@@ -438,7 +438,7 @@ test('llama.cpp blog heading clears fixed navigation on mobile', async ({ page }
|
||||
await page.goto('/blog/funasr-llama-cpp-whisper-cpp-alternative.html');
|
||||
|
||||
const layout = await page.evaluate(() => {
|
||||
const navigation = document.querySelector<HTMLElement>('nav.nav');
|
||||
const navigation = document.querySelector<HTMLElement>('.site-header');
|
||||
const heading = document.querySelector<HTMLElement>('h1');
|
||||
return {
|
||||
overflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
||||
@@ -463,7 +463,7 @@ test('legacy comparison pages keep accurate claims and fit mobile', async ({ pag
|
||||
const audit = await page.evaluate(() => ({
|
||||
overflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
||||
text: document.body.innerText,
|
||||
trackedGitHub: document.querySelector('.nav-btn')?.getAttribute('href'),
|
||||
trackedGitHub: document.querySelector('.site-header a[aria-label="GitHub"]')?.getAttribute('href'),
|
||||
}));
|
||||
|
||||
expect(audit.overflow).toBeLessThanOrEqual(1);
|
||||
|
||||
@@ -90,7 +90,8 @@ def test_visible_repository_links_use_fixed_conversion_routes(tmp_path):
|
||||
json_ld = json.loads(soup.select_one('script[type="application/ld+json"]').string)
|
||||
|
||||
assert github_hrefs == {'/go/github'}
|
||||
assert soup.select_one('.site-footer a[href="/go/docs"]')
|
||||
docs_route = '/en/docs/' if relative.startswith('en/') else '/docs/'
|
||||
assert soup.select_one(f'.site-footer a[href="{docs_route}"]')
|
||||
assert soup.select_one('.site-footer a[href="/go/releases"]')
|
||||
assert json_ld['codeRepository'] == 'https://github.com/modelscope/FunASR'
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
from pathlib import Path
|
||||
import json
|
||||
import sys
|
||||
from urllib.parse import unquote
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
import pytest
|
||||
|
||||
SITE_ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(SITE_ROOT))
|
||||
from build import build
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def output(tmp_path_factory):
|
||||
path = tmp_path_factory.mktemp('documentation')
|
||||
build(path)
|
||||
return path
|
||||
|
||||
|
||||
def test_bilingual_docs_have_navigation_and_real_source_content(output):
|
||||
for prefix in ('', 'en/'):
|
||||
index = BeautifulSoup((output / prefix / 'docs/index.html').read_text(), 'html.parser')
|
||||
assert index.select_one('[data-doc-search]')
|
||||
assert index.select_one('a[href$="/docs/moss-transcribe-diarize.html"]')
|
||||
moss = BeautifulSoup((output / prefix / 'docs/moss-transcribe-diarize.html').read_text(), 'html.parser')
|
||||
assert moss.select_one('.docs-sidebar')
|
||||
assert moss.select_one('.docs-toc a[href^="#"]')
|
||||
assert 'OpenMOSS' in moss.select_one('.docs-article').get_text()
|
||||
assert moss.select_one('.docs-article pre code')
|
||||
assert moss.select_one('[data-source-link]')['href'].endswith('.md')
|
||||
|
||||
|
||||
def test_search_index_is_bilingual_and_links_to_emitted_pages(output):
|
||||
for language in ('zh', 'en'):
|
||||
rows = json.loads((output / f'search-{language}.json').read_text())
|
||||
assert len(rows) >= 12
|
||||
assert any('MOSS' in row['title'] for row in rows)
|
||||
for row in rows:
|
||||
path = row['url'].split('#')[0].lstrip('/')
|
||||
if path.endswith('/'):
|
||||
path += 'index.html'
|
||||
assert (output / path).is_file(), row['url']
|
||||
|
||||
|
||||
def test_legacy_articles_and_new_pages_share_versioned_design(output):
|
||||
for relative in ('index.html', 'models.html', 'blog/index.html', 'donors.html', 'deploy/vllm.html'):
|
||||
soup = BeautifulSoup((output / relative).read_text(), 'html.parser')
|
||||
assert soup.select_one('link[href*="experience."][href$=".css"]'), relative
|
||||
assert soup.select_one('[data-primary-nav]'), relative
|
||||
links = soup.select('[data-primary-nav] a')
|
||||
assert links[-1]['href'].endswith('donors.html')
|
||||
home = (output / 'index.html').read_text()
|
||||
assert 'data-doc-search' in home
|
||||
assert 'Apache-2.0</dd>' not in home
|
||||
|
||||
|
||||
def test_documentation_links_are_mapped_to_local_pages_or_repository(output):
|
||||
soup = BeautifulSoup((output / 'docs/model-selection.html').read_text(), 'html.parser')
|
||||
for link in soup.select('.docs-article a[href]'):
|
||||
href = link['href']
|
||||
assert href.startswith(('/', '#', 'https://', 'http://', 'mailto:')), href
|
||||
|
||||
|
||||
def test_source_fragment_links_keep_unicode_and_punctuation_boundaries(output):
|
||||
for prefix in ('', 'en/'):
|
||||
for path in (output / prefix / 'docs').glob('*.html'):
|
||||
soup = BeautifulSoup(path.read_text(), 'html.parser')
|
||||
ids = {node['id'] for node in soup.select('[id]')}
|
||||
for link in soup.select('.docs-article a[href^="#"]'):
|
||||
assert unquote(link['href'][1:]) in ids, (path, link['href'])
|
||||
|
||||
|
||||
def test_section_based_quickstarts_are_searchable(output):
|
||||
for language, route in (('zh', '/quickstart.html'), ('en', '/en/quickstart.html')):
|
||||
rows = json.loads((output / f'search-{language}.json').read_text())
|
||||
assert any(row['url'] == route for row in rows)
|
||||
|
||||
|
||||
def test_github_pages_hubs_have_task_navigation_and_search_handoff():
|
||||
root = SITE_ROOT.parents[1]
|
||||
for relative, destination in (('index.html', '/en/docs/'), ('zh/index.html', '/docs/')):
|
||||
soup = BeautifulSoup((root / 'gh-pages-output' / relative).read_text(), 'html.parser')
|
||||
assert len(soup.select('.chapters section')) == 4
|
||||
assert soup.select_one('form[role="search"]')['action'] == 'https://www.funasr.com' + destination
|
||||
assert soup.select_one('a[href$="moss-transcribe-diarize.html"]')
|
||||
assert soup.select_one('a[href$="api.html"]')
|
||||
assert '170x' not in soup.get_text()
|
||||
@@ -74,13 +74,13 @@ def test_homepage_routes_each_project_by_workload(built_site, relative, markers)
|
||||
(
|
||||
(
|
||||
'index.html',
|
||||
'按工作流进入专用仓库',
|
||||
('/go/fun-asr', '/go/sensevoice', '/go/funclip'),
|
||||
'为你的应用,选对模型。',
|
||||
('/go/fun-asr', '/go/sensevoice', '/go/funclip', '/docs/moss-transcribe-diarize.html'),
|
||||
),
|
||||
(
|
||||
'en/index.html',
|
||||
'Choose the focused repository',
|
||||
('/go/fun-asr', '/go/sensevoice', '/go/funclip'),
|
||||
'The right model for your application.',
|
||||
('/go/fun-asr', '/go/sensevoice', '/go/funclip', '/en/docs/moss-transcribe-diarize.html'),
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -349,8 +349,8 @@ def test_old_llama_routes_point_to_product_pages(built_site):
|
||||
):
|
||||
soup = read_soup(built_site / relative)
|
||||
assert soup.select_one('link[rel="canonical"]')['href'].endswith(expected)
|
||||
assert soup.select_one('.nav-links a[href$="/deploy/"]') or soup.select_one(
|
||||
'.nav-links a[href$="/en/deploy/"]'
|
||||
assert soup.select_one('[data-primary-nav] a[href$="/deploy/"]') or soup.select_one(
|
||||
'[data-primary-nav] a[href$="/en/deploy/"]'
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -93,7 +93,9 @@ def test_vllm_contract_tracks_native_funasr_release_and_h100_validation(valid_re
|
||||
)
|
||||
limitation = entry['translations']['en']['primary_limitation'].lower()
|
||||
assert 'community-converted checkpoint' in limitation
|
||||
assert 'official funasr split-engine' in limitation
|
||||
assert 'official funaudiollm/fun-asr-nano-2512-vllm' in limitation
|
||||
assert 'does not extend the old test evidence' in limitation
|
||||
assert 'https://github.com/vllm-project/vllm/pull/54944' in evidence_urls
|
||||
|
||||
|
||||
def test_moss_transcribe_diarize_contract_tracks_third_party_upstream(valid_registry):
|
||||
|
||||
Reference in New Issue
Block a user