Merge pull request #3649 from modelscope/codex/official-native-vllm-validation-20260907

docs: document official native vLLM validation and refine deployment limitations
This commit is contained in:
zhifu gao
2026-09-07 06:33:36 +08:00
committed by GitHub
14 changed files with 1443 additions and 53 deletions
@@ -0,0 +1,624 @@
{
"schema_version": 1,
"verified": "2026-09-07",
"time_zone": "Asia/Shanghai (HTTP Date headers are UTC on 2026-09-06)",
"model": "FunAudioLLM/Fun-ASR-Nano-2512-vllm",
"revision": "a4362c943d48951f98ca2a62181cc028970270c5",
"files": [
{
"path": ".gitattributes",
"size": 2161,
"git_blob": "0bb08f0d1369b2604a2de018bd9468aa0f7bb527",
"lfs_sha256": null,
"sha256": "c6391bc5513b1407e95e1b5567f11dd7f71616ee294f85b133d397afe72d5e2d"
},
{
"path": "LICENSE",
"size": 11358,
"git_blob": "d645695673349e3947e8e5ae42332d0ac3164cd7",
"lfs_sha256": null,
"sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"
},
{
"path": "MODEL_PROVENANCE.json",
"size": 1501,
"git_blob": "7d9d3b3eaa59d42b22be085a8fb7e4d0776b3eac",
"lfs_sha256": null,
"sha256": "eba0b6a9b2a9baf53392160df38bc1d99299eb5fced854803e475303c54a1f60"
},
{
"path": "README.md",
"size": 3584,
"git_blob": "cae88395a68155067dfe0d8c28d03b591e233e31",
"lfs_sha256": null,
"sha256": "c6cfac1332d8fdf7a63db6c148a8898b7eab63b128ef9682b95ada07ef50ae18"
},
{
"path": "README_zh.md",
"size": 3330,
"git_blob": "1120b18a3831e64df88ee6fd88d73ed791a5a5b6",
"lfs_sha256": null,
"sha256": "30d462f6914cf0c83df6ee9da0a55c9ec917d3fdf5c90325dde5efb74ed095fa"
},
{
"path": "config.json",
"size": 1395,
"git_blob": "9502ac38fe453e076402a673987a181060a1402e",
"lfs_sha256": null,
"sha256": "5e72ae4baf304d65bfdf0e779be19817a71f7d65441b096f1dcff377c63ed0e5"
},
{
"path": "config.yaml",
"size": 3140,
"git_blob": "1fb7aa3cff4496048cc80d1906c82e0ca5572ac1",
"lfs_sha256": null,
"sha256": "daed38ea6484f5650fb32cbd9069b9aa13880acaf2bcb1f0bf4be2712837917c"
},
{
"path": "configuration.json",
"size": 398,
"git_blob": "2529b01f4be6622337b88ae0a24cb92c6e28dc70",
"lfs_sha256": null,
"sha256": "b64a3a55d35bcbe2cf4d31f2d3ef25a423d3ba2ebff203298c27fa055f3c7612"
},
{
"path": "convert_from_official.py",
"size": 2156,
"git_blob": "f560dbb5748e06bb44822ac4168d1bd8bce45afa",
"lfs_sha256": null,
"sha256": "1b2242ae6da997d4d3d70310cc5e1226c83ca82988555be82a03464442ce4b50"
},
{
"path": "example/en.mp3",
"size": 57441,
"git_blob": "325005e60db535f24f5b3d9504b209704031bdb4",
"lfs_sha256": null,
"sha256": "f10378336a4e584f3f63799e62f99d5add3c2a401b51d3abe7d3a3a82f255ada"
},
{
"path": "example/ja.mp3",
"size": 57837,
"git_blob": "b3056dd92c8a0210eb8f786662a4528e31b44942",
"lfs_sha256": null,
"sha256": "496dbc43b289e1d0d0cb916df9737450bca56acd8aaca046a7a2472363b1be53"
},
{
"path": "example/ko.mp3",
"size": 27909,
"git_blob": "3e67ab481230742d41e27adb63f5091cec39a7db",
"lfs_sha256": null,
"sha256": "8612f62db8319a6cb4ab4b1d2039bfc32f174f89611889ddafdeb5c0a6070b5f"
},
{
"path": "example/yue.mp3",
"size": 31246,
"git_blob": "a67e8f862132e61d867854dd5e6e4d28af57b55a",
"lfs_sha256": null,
"sha256": "5098eebc13530a66e4eac1f30d3246e65c9cfc4e096665f9d395aca8eff0d181"
},
{
"path": "example/zh.mp3",
"size": 44973,
"git_blob": "1ae2c89b29112ee5e23bcebc353ea6687c38e6bd",
"lfs_sha256": null,
"sha256": "0e64de19e4ff9a02e682955c9112f32d2317cfdbb5bc2f3504664044c993f195"
},
{
"path": "generation_config.json",
"size": 239,
"git_blob": "20a8a9156fc8c3f25295ca067f61fdf120d517c5",
"lfs_sha256": null,
"sha256": "2325da0f15bb848e018c5ae071b7943332e9f871d6b60e2ed22ca97d4cb993d2"
},
{
"path": "merges.txt",
"size": 1671853,
"git_blob": "31349551d90c7606f325fe0f11bbb8bd5fa0d7c7",
"lfs_sha256": null,
"sha256": "8831e4f1a044471340f7c0a83d7bd71306a5b867e95fd870f74d0c5308a904d5"
},
{
"path": "model.safetensors",
"size": 1970899072,
"git_blob": "7e1ee59c4a9517d8b9f4dec52a8555fc5367c184",
"lfs_sha256": "96dfbec48282dd24d3334369a01e9e909f321ee39a1b0003c528c5379f68c1a6",
"sha256": "96dfbec48282dd24d3334369a01e9e909f321ee39a1b0003c528c5379f68c1a6"
},
{
"path": "multilingual.tiktoken",
"size": 907395,
"git_blob": "ac47fe685d24565030965a94c0d98e00e0331eaa",
"lfs_sha256": null,
"sha256": "747979631e813193436aabcff7c1c235d37de8097b71c563ec8b63b7a515c718"
},
{
"path": "preprocessor_config.json",
"size": 553,
"git_blob": "b8db96b12744ebd1c9da7e70c42ae71a8ecbd9ac",
"lfs_sha256": null,
"sha256": "007aac6d52b5ecf5e75052330489138a6a8940d68cf3e9723c502e8093ab1b38"
},
{
"path": "tests/test_conversion.py",
"size": 993,
"git_blob": "f1a1e2095b6a81a49b28b424ec98cc1b9480a231",
"lfs_sha256": null,
"sha256": "262eda8e83e17b5f11c3e6e3b050327ad488688d8a4883da183bcb04332c4713"
},
{
"path": "tokenizer.json",
"size": 11422654,
"git_blob": "cd71f61a15a522601badb3dc960d800d9cb3766c",
"lfs_sha256": "aeb13307a71acd8fe81861d94ad54ab689df773318809eed3cbe794b4492dae4",
"sha256": "aeb13307a71acd8fe81861d94ad54ab689df773318809eed3cbe794b4492dae4"
},
{
"path": "tokenizer_config.json",
"size": 9965,
"git_blob": "4a31bcc54d80aaa3c6602b70979fc874957bc8e1",
"lfs_sha256": null,
"sha256": "906ee1fc8a1ffbeb04417b1fd61ea8b04debe1ae7bf6e70f7b4475712144870d"
},
{
"path": "vocab.json",
"size": 2776833,
"git_blob": "4783fe10ac3adce15ac8f358ef5462739852c569",
"lfs_sha256": null,
"sha256": "ca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910"
}
],
"audio": {
"zh": {
"path": "example/zh.mp3",
"sha256": "0e64de19e4ff9a02e682955c9112f32d2317cfdbb5bc2f3504664044c993f195",
"duration_seconds": 5.616,
"sample_rate": 48000,
"channels": 1
},
"en": {
"path": "example/en.mp3",
"sha256": "f10378336a4e584f3f63799e62f99d5add3c2a401b51d3abe7d3a3a82f255ada",
"duration_seconds": 7.176,
"sample_rate": 48000,
"channels": 1
},
"ja": {
"path": "example/ja.mp3",
"sha256": "496dbc43b289e1d0d0cb916df9737450bca56acd8aaca046a7a2472363b1be53",
"duration_seconds": 7.224,
"sample_rate": 48000,
"channels": 1
}
},
"environment": {
"python": "3.12.3",
"cuda_runtime": "12.9",
"gpu": "NVIDIA H100 80GB HBM3",
"driver": "550.127.08",
"packages": {
"z3-solver": "4.15.4.0",
"nvidia-nccl-cu12": "2.29.7",
"torchvision": "0.28.0+cu129",
"nvidia-cusparse-cu12": "12.5.10.65",
"loguru": "0.7.3",
"ninja": "1.13.0",
"requests": "2.34.2",
"mdurl": "0.1.2",
"dnspython": "2.8.0",
"tokenizers": "0.22.2",
"rpds-py": "2026.6.3",
"attrs": "26.1.0",
"soxr": "1.1.0",
"vllm": "0.27.1+cu129",
"typer": "0.27.1",
"detect-installer": "0.1.0",
"email-validator": "2.3.0",
"numpy": "2.3.5",
"setproctitle": "1.3.7",
"openai": "2.54.0",
"truststore": "0.10.4",
"fastapi-cli": "0.0.32",
"nvidia-cublas-cu12": "12.9.1.4",
"opentelemetry-exporter-otlp": "1.44.0",
"jsonschema-specifications": "2025.9.1",
"opentelemetry-exporter-otlp-proto-common": "1.44.0",
"prometheus_client": "0.26.0",
"partial-json-parser": "0.2.1.1.post7",
"astor": "0.8.1",
"aiohappyeyeballs": "2.7.1",
"depyf": "0.20.0",
"sympy": "1.14.0",
"nvidia-cuda-runtime-cu12": "12.9.79",
"nvidia-cutlass-dsl-libs-base": "4.6.0",
"anthropic": "0.121.0",
"opentelemetry-sdk": "1.44.0",
"nvidia-cutlass-dsl-libs-core": "4.6.0",
"humming-kernels": "0.1.10",
"rich": "15.0.0",
"pybase64": "1.5.0",
"ijson": "3.5.1",
"apache-tvm-ffi": "0.1.11",
"PyYAML": "6.0.3",
"pyzmq": "27.1.0",
"frozenlist": "1.8.0",
"multidict": "6.7.1",
"jmespath": "1.1.0",
"opentelemetry-semantic-conventions-ai": "0.5.1",
"nvidia-ml-py": "13.610.43",
"nvidia-cuda-nvdisasm": "13.3.73",
"docstring_parser": "0.18.0",
"lm-format-enforcer": "0.11.3",
"mcp": "2.0.0",
"urllib3": "2.7.0",
"huggingface_hub": "1.27.0",
"torchcodec": "0.15.0+cu129",
"anyio": "4.14.2",
"nvidia-cutlass-dsl-libs-cu12": "4.6.0",
"typing_extensions": "4.16.0",
"setuptools": "80.10.2",
"cffi": "2.1.1",
"mcp-types": "2.0.0",
"aiosignal": "1.4.0",
"jiter": "0.16.0",
"python-dotenv": "1.2.2",
"fastar": "0.11.0",
"cuda-toolkit": "12.9.1",
"six": "1.17.0",
"tqdm": "4.70.0",
"sniffio": "1.3.1",
"cuda-bindings": "12.9.7",
"sentry-sdk": "2.67.1",
"MarkupSafe": "3.0.3",
"nvidia-cudnn-frontend": "1.27.0",
"PyJWT": "2.13.0",
"llguidance": "1.7.6",
"fastsafetensors": "0.3.3",
"nccl4py": "0.4.1",
"referencing": "0.37.0",
"nvidia-cusparselt-cu12": "0.8.1",
"numba": "0.65.0",
"lark": "1.2.2",
"rignore": "0.8.1",
"fastapi-cloud-cli": "0.23.0",
"propcache": "0.5.2",
"pip": "24.0",
"torch": "2.13.0+cu129",
"cuda-core": "1.1.1",
"certifi": "2026.7.22",
"psutil": "7.2.2",
"pycparser": "3.0",
"triton": "3.7.1",
"starlette": "1.6.0",
"cryptography": "50.0.0",
"nvidia-curand-cu12": "10.3.10.19",
"python-multipart": "0.0.32",
"tabulate": "0.10.0",
"Pygments": "2.20.0",
"outlines_core": "0.2.14",
"cuda-python": "12.9.7",
"pillow": "12.3.0",
"shellingham": "1.5.4",
"httptools": "0.8.0",
"pynvvideocodec": "2.0.4",
"transformers": "5.15.0",
"msgspec": "0.21.1",
"httpx2": "2.10.0",
"dill": "0.4.1",
"nvidia-cuda-crt": "13.3.73",
"packaging": "26.0",
"regex": "2026.7.19",
"nvidia-nvshmem-cu12": "3.4.5",
"uvicorn": "0.52.1",
"cuda-tile": "1.5.0",
"nvidia-cuda-nvrtc": "13.3.33",
"pydantic-extra-types": "2.11.1",
"ml_dtypes": "0.5.4",
"prometheus-fastapi-instrumentator": "8.1.0",
"opentelemetry-api": "1.44.0",
"pydantic": "2.13.4",
"Jinja2": "3.1.6",
"pycountry": "26.2.16",
"filelock": "3.32.2",
"opencv-python-headless": "5.0.0.93",
"tokenspeed-triton": "3.8.10.post20260721",
"sse-starlette": "3.4.8",
"torchaudio": "2.11.0+cu129",
"nvidia-cutlass-dsl-libs-cu13": "4.6.0",
"protobuf": "6.33.6",
"cbor2": "6.1.4",
"pydantic_core": "2.46.4",
"flashinfer-python": "0.6.16.post3",
"googleapis-common-protos": "1.75.1",
"nvidia-nvvm": "13.3.73",
"hf-xet": "1.6.0",
"nvidia-cudnn-cu12": "9.20.0.48",
"nvidia-nvjitlink-cu12": "12.9.86",
"yarl": "1.24.5",
"nvidia-cuda-cccl": "13.3.3.4.1",
"charset-normalizer": "3.5.0",
"interegular": "0.3.3",
"h11": "0.16.0",
"xgrammar": "0.2.3",
"nvidia-cutlass-dsl": "4.6.0",
"quack-kernels": "0.6.1",
"nvidia-cuda-nvcc": "13.3.73",
"opentelemetry-proto": "1.44.0",
"httpcore": "1.0.9",
"annotated-types": "0.8.0",
"nvidia-cusolver-cu12": "11.7.5.82",
"rich-toolkit": "0.20.3",
"mistral_common": "1.11.7",
"nvtx": "0.2.15",
"distro": "1.9.0",
"tokenspeed-mla": "0.1.8",
"supervisor": "4.3.0",
"nvidia-nvtx-cu12": "12.9.79",
"grpcio": "1.83.0",
"nvidia-cufft-cu12": "11.4.1.4",
"model-hosting-container-standards": "0.1.16",
"av": "18.1.0",
"pydantic-settings": "2.15.0",
"uvloop": "0.22.1",
"httpx": "0.28.1",
"nvidia-cufile-cu12": "1.14.1.1",
"opentelemetry-semantic-conventions": "0.65b0",
"opentelemetry-exporter-otlp-proto-grpc": "1.44.0",
"typing-inspection": "0.4.4",
"markdown-it-py": "4.2.0",
"tiktoken": "0.13.0",
"python-json-logger": "4.1.0",
"nvidia-cuda-runtime": "13.3.29",
"websockets": "17.0.1",
"httpcore2": "2.10.0",
"torch_c_dlpack_ext": "0.1.5",
"opentelemetry-exporter-otlp-proto-http": "1.44.0",
"cloudpickle": "3.1.2",
"safetensors": "0.8.0",
"tilelang": "0.1.12",
"compressed-tensors": "0.17.0",
"nvidia-cuda-cupti-cu12": "12.9.79",
"soundfile": "0.14.0",
"einops": "0.8.2",
"fastapi": "0.136.3",
"cuda-pathfinder": "1.6.0",
"aiohttp": "3.14.3",
"networkx": "3.6.1",
"fsspec": "2026.7.0",
"watchfiles": "1.2.0",
"llvmlite": "0.47.0",
"annotated-doc": "0.0.5",
"pyelftools": "0.33",
"py-cpuinfo": "9.0.0",
"openai-harmony": "0.0.8",
"mpmath": "1.3.0",
"blake3": "1.0.9",
"sentencepiece": "0.2.2",
"click": "8.4.2",
"nvidia-cuda-nvrtc-cu12": "12.9.86",
"scipy": "1.18.0",
"jsonschema": "4.26.0",
"cachetools": "7.1.7",
"idna": "3.18",
"more-itertools": "10.8.0",
"importlib_metadata": "8.7.1",
"jaraco.text": "4.0.0",
"wheel": "0.46.3",
"tomli": "2.4.0",
"platformdirs": "4.4.0",
"zipp": "3.23.0",
"jaraco.functools": "4.4.0",
"jaraco.context": "6.1.0",
"backports.tarfile": "1.2.0",
"autocommand": "2.2.2"
},
"native_source": "vllm/model_executor/models/funasr.py",
"native_source_sha256": "0cc059845327614a82259c0fe7bd5a1f868565e7e011c113b24c18a47f79c5c7"
},
"serving": {
"model_directory": "$MODEL_DIR",
"served_model": "fun-asr-nano-official-a4362c94",
"host": "127.0.0.1",
"port": 57185,
"dtype": "float32",
"gpu_memory_utilization": 0.4,
"enforce_eager": true,
"resolved_max_model_len": 40960
},
"commands": {
"install": [
"export VLLM_PYTHON=\"$PWD/.venv/bin/python\"\nexport VALIDATION_DIR=\"$PWD/.official-native-validation\"\nexport MODEL_DIR=\"$VALIDATION_DIR/official-model/a4362c943d48951f98ca2a62181cc028970270c5\"",
"\"$VLLM_PYTHON\" - <<'PY'\nimport importlib.metadata as metadata\nimport torch\nimport vllm.model_executor.models.funasr\n\nfor name, expected in {\"vllm\": \"0.27.1+cu129\", \"torch\": \"2.13.0+cu129\",\n \"transformers\": \"5.15.0\"}.items():\n assert metadata.version(name) == expected, (name, metadata.version(name))\nassert torch.version.cuda == \"12.9\" and torch.cuda.is_available()\nPY",
"HF_HUB_DISABLE_TELEMETRY=1 HF_XET_CACHE=\"$VALIDATION_DIR/isolated-xet-cache\" \"$VLLM_PYTHON\" - <<'PY'\nimport hashlib\nimport json\nimport os\nfrom pathlib import Path\nfrom huggingface_hub import HfApi, snapshot_download\n\nmodel_id = \"FunAudioLLM/Fun-ASR-Nano-2512-vllm\"\nrevision = \"a4362c943d48951f98ca2a62181cc028970270c5\"\nroot = Path(os.environ[\"MODEL_DIR\"])\ninfo = HfApi().model_info(model_id, revision=revision, files_metadata=True)\nassert info.sha == revision and len(info.siblings) == 23\nsnapshot_download(model_id, revision=revision, local_dir=str(root),\n cache_dir=str(Path(os.environ[\"VALIDATION_DIR\"]) / \"isolated-hf-cache\"),\n max_workers=4)\nfiles = []\nfor item in info.siblings:\n content = (root / item.rfilename).read_bytes()\n digest = hashlib.sha256(content).hexdigest()\n assert len(content) == item.size, item.rfilename\n if item.lfs:\n assert digest == item.lfs.sha256, item.rfilename\n else:\n assert hashlib.sha1(f\"blob {len(content)}\\0\".encode() + content).hexdigest() == item.blob_id\n files.append({\"path\": item.rfilename, \"size\": len(content), \"sha256\": digest})\nassert json.loads((root / \"config.json\").read_text())[\"architectures\"] == [\"FunASRForConditionalGeneration\"]\n(root.parent / \"download-manifest.json\").write_text(json.dumps(files, indent=2) + \"\\n\")\nPY"
],
"launch": [
"CUDA_VISIBLE_DEVICES=0 PYTHONDONTWRITEBYTECODE=1 \\\nHF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 \\\nHF_HUB_DISABLE_IMPLICIT_TOKEN=1 HF_HUB_DISABLE_TELEMETRY=1 VLLM_NO_USAGE_STATS=1 \\\nVLLM_CACHE_ROOT=\"$VALIDATION_DIR/runtime-cache\" TRITON_CACHE_DIR=\"$VALIDATION_DIR/triton-cache\" \\\n\"$VLLM_PYTHON\" -B -m vllm.entrypoints.openai.api_server \\\n --model \"$MODEL_DIR\" --served-model-name fun-asr-nano-official-a4362c94 \\\n --host 127.0.0.1 --port 57185 --dtype float32 \\\n --gpu-memory-utilization 0.40 --enforce-eager"
],
"health": [
"curl --max-time 15 -fsS http://127.0.0.1:57185/health",
"curl --max-time 15 -fsS http://127.0.0.1:57185/v1/models"
],
"smoke": [
"curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F \"file=@$MODEL_DIR/example/zh.mp3;type=audio/mpeg\" -F model=fun-asr-nano-official-a4362c94 -F language=zh -F response_format=json",
"curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F \"file=@$MODEL_DIR/example/en.mp3;type=audio/mpeg\" -F model=fun-asr-nano-official-a4362c94 -F language=en -F response_format=json",
"curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F \"file=@$MODEL_DIR/example/ja.mp3;type=audio/mpeg\" -F model=fun-asr-nano-official-a4362c94 -F language=ja -F response_format=json",
"curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F \"file=@$MODEL_DIR/example/zh.mp3;type=audio/mpeg\" -F model=fun-asr-nano-official-a4362c94 -F language=zh -F 'hotwords=开放时间,开放时间,开放时间' -F response_format=json"
]
},
"http": {
"seconds_to_health": 84.12324620783329,
"requests": [
{
"method": "GET",
"path": "/health",
"request_fields": null,
"status": 200,
"headers": {
"date": "Sun, 06 Sep 2026 21:05:28 GMT",
"server": "uvicorn",
"content-length": "0"
},
"seconds": 0.001022782176733017,
"response_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"body_file": "health.body",
"raw_body_utf8": ""
},
{
"method": "GET",
"path": "/v1/models",
"request_fields": null,
"status": 200,
"headers": {
"date": "Sun, 06 Sep 2026 21:05:28 GMT",
"server": "uvicorn",
"content-length": "571",
"content-type": "application/json"
},
"seconds": 0.0025825612246990204,
"response_sha256": "8b4d500ad70f80251bc5d7535f3b037fb2001ba13a44a1d1e0bfa82918a6f9b7",
"body_file": "models.body"
},
{
"method": "POST",
"path": "/v1/audio/transcriptions",
"request_fields": {
"model": "fun-asr-nano-official-a4362c94",
"language": "zh",
"response_format": "json"
},
"status": 200,
"headers": {
"date": "Sun, 06 Sep 2026 21:05:28 GMT",
"server": "uvicorn",
"content-length": "93",
"content-type": "application/json"
},
"seconds": 0.8895468302071095,
"response_sha256": "a81ea2be2bf7b823ecf880d8c66c67ce6b1c6d12542cd4529008a07e2c40c22e",
"body_file": "transcription-zh.body",
"text": "开饭时间早上九点至下午五点。",
"raw_body_utf8": "{\"text\":\"开饭时间早上九点至下午五点。\",\"usage\":{\"type\":\"duration\",\"seconds\":6}}"
},
{
"method": "POST",
"path": "/v1/audio/transcriptions",
"request_fields": {
"model": "fun-asr-nano-official-a4362c94",
"language": "en",
"response_format": "json"
},
"status": 200,
"headers": {
"date": "Sun, 06 Sep 2026 21:05:29 GMT",
"server": "uvicorn",
"content-length": "136",
"content-type": "application/json"
},
"seconds": 0.3863455504179001,
"response_sha256": "4530a812ce88e903ee6196469cef1ffba991d035e6272b145cb487edf12a8bcb",
"body_file": "transcription-en.body",
"text": "The tribal chieftain called for the boy, and presented him with fifty pieces of gold.",
"raw_body_utf8": "{\"text\":\"The tribal chieftain called for the boy, and presented him with fifty pieces of gold.\",\"usage\":{\"type\":\"duration\",\"seconds\":8}}"
},
{
"method": "POST",
"path": "/v1/audio/transcriptions",
"request_fields": {
"model": "fun-asr-nano-official-a4362c94",
"language": "ja",
"response_format": "json"
},
"status": 200,
"headers": {
"date": "Sun, 06 Sep 2026 21:05:29 GMT",
"server": "uvicorn",
"content-length": "162",
"content-type": "application/json"
},
"seconds": 0.47393732890486717,
"response_sha256": "d5c3f6c5ccbc5d026cbfcd00839b2ec8b5bc2a296fda877595c8ed27f831fd6c",
"body_file": "transcription-ja.body",
"text": "うちの中学は弁当制で、持っていけない場合は、五十円の学校販売のパンを買う。",
"raw_body_utf8": "{\"text\":\"うちの中学は弁当制で、持っていけない場合は、五十円の学校販売のパンを買う。\",\"usage\":{\"type\":\"duration\",\"seconds\":8}}"
},
{
"method": "POST",
"path": "/v1/audio/transcriptions",
"request_fields": {
"model": "fun-asr-nano-official-a4362c94",
"language": "zh",
"response_format": "json",
"hotwords": "开放时间,开放时间,开放时间"
},
"status": 200,
"headers": {
"date": "Sun, 06 Sep 2026 21:05:30 GMT",
"server": "uvicorn",
"content-length": "93",
"content-type": "application/json"
},
"seconds": 0.19060998782515526,
"response_sha256": "972701daeae831b0fe97b4ace846c3cee997cdaab7c31d79e89e2c3dd0856a94",
"body_file": "transcription-zh-hotwords.body",
"text": "开放时间早上九点至下午五点。",
"raw_body_utf8": "{\"text\":\"开放时间早上九点至下午五点。\",\"usage\":{\"type\":\"duration\",\"seconds\":6}}"
},
{
"method": "POST",
"path": "/v1/audio/transcriptions",
"request_fields": {
"model": "fun-asr-nano-official-a4362c94",
"language": "en",
"response_format": "json"
},
"status": 200,
"headers": {
"date": "Sun, 06 Sep 2026 21:05:30 GMT",
"server": "uvicorn",
"content-length": "136",
"content-type": "application/json"
},
"seconds": 0.7999004796147346,
"response_sha256": "4530a812ce88e903ee6196469cef1ffba991d035e6272b145cb487edf12a8bcb",
"body_file": "concurrent-en.body",
"text": "The tribal chieftain called for the boy, and presented him with fifty pieces of gold.",
"raw_body_utf8": "{\"text\":\"The tribal chieftain called for the boy, and presented him with fifty pieces of gold.\",\"usage\":{\"type\":\"duration\",\"seconds\":8}}"
},
{
"method": "POST",
"path": "/v1/audio/transcriptions",
"request_fields": {
"model": "fun-asr-nano-official-a4362c94",
"language": "ja",
"response_format": "json"
},
"status": 200,
"headers": {
"date": "Sun, 06 Sep 2026 21:05:30 GMT",
"server": "uvicorn",
"content-length": "162",
"content-type": "application/json"
},
"seconds": 0.9049098119139671,
"response_sha256": "d5c3f6c5ccbc5d026cbfcd00839b2ec8b5bc2a296fda877595c8ed27f831fd6c",
"body_file": "concurrent-ja.body",
"text": "うちの中学は弁当制で、持っていけない場合は、五十円の学校販売のパンを買う。",
"raw_body_utf8": "{\"text\":\"うちの中学は弁当制で、持っていけない場合は、五十円の学校販売のパンを買う。\",\"usage\":{\"type\":\"duration\",\"seconds\":8}}"
}
],
"two_request_wall_seconds": 0.9112209342420101
},
"clean_install_validated": false,
"first_chinese_request_warmed": false,
"guard_failure_before_server_spawn": true,
"guard_correction": "Match native vLLM import context before package inventory comparison; no dependency changes; first actual server launch succeeded.",
"cleanup": {
"all_owned_exited": true,
"server_exit_code": 0,
"loopback_port_closed": true,
"no_gpu_compute_processes": true,
"packages_unchanged": true,
"native_source_unchanged": true
},
"redactions": "Private host, user paths, GPU UUID and PIDs omitted. HTTP response_sha256 values hash original raw bytes; models.body contains a private local root and is retained privately, not embedded. Public commands substitute portable directory variables for original absolute paths; they are not an independently tested clean installer.",
"limitations": "Short functional compatibility probe only. First sequential Chinese call is not warmed. No accuracy, production capacity, or historical-benchmark equivalence claim."
}
+1 -1
View File
@@ -14,7 +14,7 @@ Use this page to choose the shortest deployment path for a product, demo, benchm
| Kubernetes API | Internal speech API for cluster services | [Kubernetes template](../examples/openai_api/kubernetes/) | Starts as private `ClusterIP`; add auth, TLS, network policy, and GPU scheduling before broader exposure. |
| Runtime WebSocket service | Live captions, meetings, call-center streams | [Runtime service docs](../runtime/readme.md) | Use when partial results, endpointing, or long-lived audio streams matter. |
| ONNX/C++ runtime | High-concurrency CPU services or embedded realtime ASR | [ONNX runtime docs](../runtime/onnxruntime/readme.md) | Keep this path when latency/concurrency is already proven; add text post-processing for fixed business terms before moving to GPU LLMs. |
| vLLM acceleration | Higher-throughput LLM-based ASR with Fun-ASR-Nano | [vLLM guide](./vllm_guide.md) | Use for LLM decoder throughput; does not apply to non-autoregressive Paraformer. |
| vLLM acceleration | Native file transcription or split-engine LLM decoding with Fun-ASR-Nano | [Official native validation](./vllm_official_native_validation.md); [split-engine guide](./vllm_guide.md) | Native: official a4362c94 local snapshot, vLLM 0.27.1, existing-environment functional smoke only, not clean-install or capacity validation. Does not apply to non-autoregressive Paraformer. |
| MOSS-Transcribe-Diarize | Long-form multi-speaker transcription with timestamps and speaker labels | [Third-party MOSS deployment guide](./moss_transcribe_diarize.md) | OpenMOSS Apache-2.0 model integrated with FunASR `AutoModel`; choose local HF (`backend="hf"`), vLLM (`backend="vllm"`), or SGLang Omni (`backend="sglang"`). The model remains published and maintained by OpenMOSS. |
| MCP server | Claude/Cursor/desktop agent speech tools | [MCP example](../examples/mcp_server/) | Good when the ASR result should be exposed as a local tool. |
| Subtitle generator | SRT/VTT from long audio or video | [Subtitle example](../examples/subtitle/) | Use verbose segments and speaker labels when readability matters. |
+1 -1
View File
@@ -14,7 +14,7 @@
| Kubernetes API | 集群内私有语音 API | [Kubernetes 模板](../examples/openai_api/kubernetes/README_zh.md) | 默认私有 `ClusterIP`;对外开放前补齐鉴权、TLS、网络策略和 GPU 调度。 |
| Runtime WebSocket 服务 | 实时字幕、会议、客服流式音频 | [Runtime 服务文档](../runtime/readme_cn.md) | 需要中间结果、断句或长连接音频流时选择。 |
| ONNX/C++ Runtime | 高并发 CPU 服务或嵌入式实时 ASR | [ONNX Runtime 文档](../runtime/onnxruntime/readme.md) | 如果延迟和并发已经验证,不要轻易替换;固定业务词优先做文本后处理。 |
| vLLM 加速 | Fun-ASR-Nano 等 LLM-based ASR 高吞吐 | [vLLM 指南](./vllm_guide.md) | 适合 LLM 解码吞吐;不适用于非自回归 Paraformer。 |
| vLLM 加速 | Fun-ASR-Nano 原生文件转写或 split-engine LLM 解码 | [官方原生验证](./vllm_official_native_validation_zh.md);[split-engine 指南](./vllm_guide_zh.md) | 原生路径固定官方 a4362c94 本地快照、vLLM 0.27.1,仅为既有环境功能验证,不是全新安装或容量验证;不适用于非自回归 Paraformer。 |
| MOSS-Transcribe-Diarize | 长音频多人转写、时间戳和说话人标签 | [第三方 MOSS 部署指南](./moss_transcribe_diarize.md) | OpenMOSS Apache-2.0 模型,已接入 FunASR `AutoModel`;可选本地 HF(`backend="hf"`)、vLLM(`backend="vllm"`)或 SGLang Omni(`backend="sglang"`)。模型仍由 OpenMOSS 发布和维护。 |
| MCP 服务 | Claude/Cursor/桌面 Agent 语音工具 | [MCP 示例](../examples/mcp_server/) | 适合把 ASR 结果暴露成一个本地工具。 |
| 字幕生成 | 从长音频或视频生成 SRT/VTT | [字幕示例](../examples/subtitle/) | 需要可读性时使用 verbose segments 和说话人标签。 |
+3 -1
View File
@@ -45,7 +45,9 @@ entry to these guides. Source Markdown remains in this repository.
./moss_transcribe_diarize_zh.md
vllm_guide
vllm_guide_zh
vllm_native_funasr_validation
vllm_official_native_validation
vllm_official_native_validation_zh
Historical community vLLM validation (2026-08-13) <vllm_native_funasr_validation>
.. toctree::
:maxdepth: 1
+250
View File
@@ -0,0 +1,250 @@
# Official native vLLM validation: Fun-ASR-Nano
[中文](vllm_official_native_validation_zh.md)
Verified **2026-09-07, Asia/Shanghai** (raw HTTP Date headers: 2026-09-06 UTC).
This is a separate functional serving record for the official
[FunAudioLLM/Fun-ASR-Nano-2512-vllm snapshot](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512-vllm/tree/a4362c943d48951f98ca2a62181cc028970270c5),
revision `a4362c943d48951f98ca2a62181cc028970270c5`.
It is a **model revision, not a root FunASR package version**. This run used
vLLM's native `FunASRForConditionalGeneration`, not FunASR AutoModel or the
[split-engine decoder path](vllm_guide.md).
The [2026-08-13 historical community validation](vllm_native_funasr_validation.md)
and its community `allendou/Fun-ASR-Nano-2512-vllm@e718b36e` benchmark remain
unchanged. The official run is not a relabeling of those timings or evidence of
a speed improvement.
## Tested scope and environment
Eight recorded HTTP requests returned 200: health, model discovery, three
single-language transcriptions, Chinese hotwords, and two concurrent requests.
All 23 files were downloaded directly from the pinned official repository,
verified against Hub Git/LFS digests, copied to a durable backup, and rehashed.
The test did not substitute community cache files or modify upstream model code.
| Component | Observed value |
| --- | --- |
| Python | 3.12.3 |
| vLLM | 0.27.1+cu129 |
| Torch | 2.13.0+cu129 |
| Transformers | 5.15.0 |
| CUDA runtime / NVIDIA driver | 12.9 / 550.127.08 |
| GPU | One NVIDIA H100 80GB HBM3 |
| Audio libraries | av 18.1.0, soundfile 0.14.0, scipy 1.18.0, soxr 1.1.0, NumPy 2.3.5 |
| Hub tooling / HTTP client | huggingface_hub 1.27.0 / requests 2.34.2 |
| Server | FP32, eager, GPU memory utilization 0.40, resolved max model length 40,960 |
This validated an **existing environment, not a clean installation**. No packages
were installed, upgraded or re-resolved. These observed versions are not a
newly validated dependency lockfile. In particular, do not infer that an
unconstrained `pip install vllm` or a floating Hub loader reproduces this run.
[vLLM #54944](https://github.com/vllm-project/vllm/pull/54944) merged at
`e473e9036f979d546830aece9855027049faf0ba` on 2026-09-05. It updates the
supported-model documentation and test registry to the official checkpoint,
not the inference implementation. At the 2026-09-07 audit, main used the
official reference but v0.28.0 still referenced the community artifact.
Merged does not mean released. This run validates neither main nor v0.28.0,
and does not override the upstream test registry's separate Transformers
version constraint.
## Prepare a pinned local snapshot
The following commands are a portable transcription of the tested preparation
and launch procedure: private absolute paths are replaced by variables.
Point `VLLM_PYTHON` at an already provisioned environment matching the table;
the `.venv` path below is a placeholder, not a venv creation command.
Use a new isolated validation directory and keep its download manifest.
Existing Hub authentication may be used without displaying credentials.
```sh
export VLLM_PYTHON="$PWD/.venv/bin/python"
export VALIDATION_DIR="$PWD/.official-native-validation"
export MODEL_DIR="$VALIDATION_DIR/official-model/a4362c943d48951f98ca2a62181cc028970270c5"
```
Check versions in the same native-import context used during validation:
```sh
"$VLLM_PYTHON" - <<'PY'
import importlib.metadata as metadata
import torch
import vllm.model_executor.models.funasr
for name, expected in {"vllm": "0.27.1+cu129", "torch": "2.13.0+cu129",
"transformers": "5.15.0"}.items():
assert metadata.version(name) == expected, (name, metadata.version(name))
assert torch.version.cuda == "12.9" and torch.cuda.is_available()
PY
```
Download all 23 files at the immutable revision and verify the official Hub
metadata before serving. The script does not execute the downloaded conversion
script and does not modify the snapshot:
```sh
HF_HUB_DISABLE_TELEMETRY=1 HF_XET_CACHE="$VALIDATION_DIR/isolated-xet-cache" "$VLLM_PYTHON" - <<'PY'
import hashlib
import json
import os
from pathlib import Path
from huggingface_hub import HfApi, snapshot_download
model_id = "FunAudioLLM/Fun-ASR-Nano-2512-vllm"
revision = "a4362c943d48951f98ca2a62181cc028970270c5"
root = Path(os.environ["MODEL_DIR"])
info = HfApi().model_info(model_id, revision=revision, files_metadata=True)
assert info.sha == revision and len(info.siblings) == 23
snapshot_download(model_id, revision=revision, local_dir=str(root),
cache_dir=str(Path(os.environ["VALIDATION_DIR"]) / "isolated-hf-cache"),
max_workers=4)
files = []
for item in info.siblings:
content = (root / item.rfilename).read_bytes()
digest = hashlib.sha256(content).hexdigest()
assert len(content) == item.size, item.rfilename
if item.lfs:
assert digest == item.lfs.sha256, item.rfilename
else:
assert hashlib.sha1(f"blob {len(content)}\0".encode() + content).hexdigest() == item.blob_id
files.append({"path": item.rfilename, "size": len(content), "sha256": digest})
assert json.loads((root / "config.json").read_text())["architectures"] == ["FunASRForConditionalGeneration"]
(root.parent / "download-manifest.json").write_text(json.dumps(files, indent=2) + "\n")
PY
```
### Checkpoint and sample hashes
All examples are mono 48 kHz MP3. Container durations measured by ffprobe were
**zh 5.616 s, en 7.176 s, ja 7.224 s**. Server usage counters rounded those to
6/8/8 seconds; those rounded values are not the measured file durations.
| File | Bytes | SHA256 |
| --- | --- | --- |
| `example/en.mp3` | 57441 | `f10378336a4e584f3f63799e62f99d5add3c2a401b51d3abe7d3a3a82f255ada` |
| `example/ja.mp3` | 57837 | `496dbc43b289e1d0d0cb916df9737450bca56acd8aaca046a7a2472363b1be53` |
| `example/zh.mp3` | 44973 | `0e64de19e4ff9a02e682955c9112f32d2317cfdbb5bc2f3504664044c993f195` |
| `model.safetensors` | 1970899072 | `96dfbec48282dd24d3334369a01e9e909f321ee39a1b0003c528c5379f68c1a6` |
The [reproducibility metadata](benchmark/vllm_official_native_20260907.json)
contains all 23 file sizes, SHA256 and upstream Git/LFS digests, observed package
versions, exact HTTP fields, original response hashes and unrounded timings.
Private host paths, GPU identifiers and credentials are not published. In
particular, the raw `/v1/models` response's local root is retained in private
evidence; its digest refers to the original bytes, not a redacted replacement.
## Launch offline on loopback
First ensure GPU 0 is idle and loopback port 57185 is free. Choose another free
port consistently in every command if necessary; only 57185 was used in this
record. Run in the foreground from the prepared shell:
```sh
CUDA_VISIBLE_DEVICES=0 PYTHONDONTWRITEBYTECODE=1 \
HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 \
HF_HUB_DISABLE_IMPLICIT_TOKEN=1 HF_HUB_DISABLE_TELEMETRY=1 VLLM_NO_USAGE_STATS=1 \
VLLM_CACHE_ROOT="$VALIDATION_DIR/runtime-cache" TRITON_CACHE_DIR="$VALIDATION_DIR/triton-cache" \
"$VLLM_PYTHON" -B -m vllm.entrypoints.openai.api_server \
--model "$MODEL_DIR" --served-model-name fun-asr-nano-official-a4362c94 \
--host 127.0.0.1 --port 57185 --dtype float32 \
--gpu-memory-utilization 0.40 --enforce-eager
```
The engine loaded the verified **local snapshot** with Hub and Transformers
offline. No `--trust-remote-code` or floating model ID loader was used.
Startup to healthy took 84.123246 s; download was already complete. This startup
observation is not a startup performance guarantee.
In a second shell, set `VALIDATION_DIR` and `MODEL_DIR` to the same absolute
paths as above, then wait for readiness:
```sh
curl --max-time 15 -fsS http://127.0.0.1:57185/health
curl --max-time 15 -fsS http://127.0.0.1:57185/v1/models
```
`/v1/models` must contain ID `fun-asr-nano-official-a4362c94` and a root equal
to your resolved `MODEL_DIR`. The recorded run checked both.
## Actual transcription requests
The harness used Python requests with multipart audio MIME `audio/mpeg`,
connect/read timeouts of 5/45 s, explicit `language=zh`, `language=en` and
`language=ja`, and `response_format=json`. It sent no temperature or
generation-length override. These equivalent curl requests preserve those
fields; their timings have not been separately measured:
```sh
curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F "file=@$MODEL_DIR/example/zh.mp3;type=audio/mpeg" -F model=fun-asr-nano-official-a4362c94 -F language=zh -F response_format=json
curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F "file=@$MODEL_DIR/example/en.mp3;type=audio/mpeg" -F model=fun-asr-nano-official-a4362c94 -F language=en -F response_format=json
curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F "file=@$MODEL_DIR/example/ja.mp3;type=audio/mpeg" -F model=fun-asr-nano-official-a4362c94 -F language=ja -F response_format=json
curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F "file=@$MODEL_DIR/example/zh.mp3;type=audio/mpeg" -F model=fun-asr-nano-official-a4362c94 -F language=zh -F 'hotwords=开放时间,开放时间,开放时间' -F response_format=json
```
Observed texts:
- Chinese baseline: 开饭时间早上九点至下午五点。
- English: The tribal chieftain called for the boy, and presented him with fifty pieces of gold.
- Japanese: うちの中学は弁当制で、持っていけない場合は、五十円の学校販売のパンを買う。
- Chinese with `hotwords=开放时间,开放时间,开放时间`: 开放时间早上九点至下午五点。
The baseline error is preserved. Repeating this hotword changed this sample's
output; it is not a general hotword policy or an accuracy guarantee.
| Recorded request | HTTP | Client wall seconds |
| --- | --- | --- |
| GET /health | 200 | 0.001023 |
| GET /v1/models | 200 | 0.002583 |
| POST zh (first) | 200 | 0.889547 |
| POST en | 200 | 0.386346 |
| POST ja | 200 | 0.473937 |
| POST zh + hotwords | 200 | 0.190610 |
| Concurrent en | 200 | 0.799900 |
| Concurrent ja | 200 | 0.904910 |
The first Chinese call was **not warmed**: it was the first transcription
after health and model discovery. Later calls reused that engine.
Times include local HTTP and decoding, exclude model download and startup,
and are single observations, not latency distributions.
After the four sequential transcription calls, a two-worker
`ThreadPoolExecutor` sent the same English and Japanese multipart requests.
Both returned 200 with the same texts. The combined wall time was
**0.9112209342420101 s (0.911 s)**, including executor setup and waiting for both
results. This is only a **two-request functional concurrency probe**, not
throughput, production capacity, an accuracy study or a comparison with the
historical 1.123 s community probe.
## Harness boundary and cleanup
The first harness attempt failed a package-inventory guard **before server
spawn**. Preparation imported native vLLM, which exposed setuptools vendored
distributions on `sys.path`; the original serve phase compared a different
import context. Matching that import produced an empty package difference.
The original failure and correction were preserved. An early failure status
flag incorrectly said the server had started; process evidence and the
correction establish that it had not. No dependency or model-code changes were
needed. The first actual server launch then completed the eight-request smoke.
The bounded harness terminated its own process group and waited for the
server and children. Server exit code was 0, the loopback port closed, and no
GPU compute processes remained. Package and native source hashes were
unchanged. A separate check confirmed all 23 original and backup file hashes,
eight raw responses, model identity and cleanup. When reproducing manually,
stop the foreground server after the probes and verify its workers and port
are gone; do not terminate unrelated GPU jobs.
## Deployment boundaries
This is request/response `/v1/audio/transcriptions`, not a validated
`/v1/realtime` streaming session. Long audio, speaker diarization, timestamp
accuracy, other GPUs, sustained load and production capacity were not tested.
Do not infer a FunASR SDK or package-release validation from this native server.
Keep the worker on `127.0.0.1`. Before exposing an API, use a gateway for
authentication, TLS, rate limits and audio size/duration limits, with isolated
uploads and a retention policy. That gateway was not part of this smoke.
See the [deployment matrix](deployment_matrix.md) and
[security boundaries](../examples/openai_api/SECURITY.md).
+231
View File
@@ -0,0 +1,231 @@
# 官方 checkpoint 原生 vLLM 验证记录:Fun-ASR-Nano
[English](vllm_official_native_validation.md)
验证日期:**2026-09-07,Asia/Shanghai**,原始 HTTP Date 头对应 2026-09-06 UTC。
本记录独立验证官方
[FunAudioLLM/Fun-ASR-Nano-2512-vllm 快照](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512-vllm/tree/a4362c943d48951f98ca2a62181cc028970270c5),
不可变 revision 为 `a4362c943d48951f98ca2a62181cc028970270c5`。
这是**模型 revision,不是根目录 FunASR Python 包版本**。本次使用 vLLM 原生
`FunASRForConditionalGeneration`,不经过 FunASR AutoModel,也不是
[split-engine 解码器路径](vllm_guide_zh.md)。
[2026-08-13 社区 checkpoint 历史记录](vllm_native_funasr_validation.md)
及其 `allendou/Fun-ASR-Nano-2512-vllm@e718b36e` benchmark 保持原样。
本次不是给旧数据换模型名称,也不说明官方模型更快。
## 验证范围与环境
8 个已记录 HTTP 请求均返回 200:健康检查、模型列表、中英日单请求转写、
中文热词请求,以及两个并发请求。全部 23 个文件直接从官方固定 revision 下载,
按 Hub Git/LFS 摘要验证,复制到持久备份后再次校验。
没有替换为社区缓存文件,也没有修改上游模型代码。
| 组件 | 实测值 |
| --- | --- |
| Python | 3.12.3 |
| vLLM | 0.27.1+cu129 |
| Torch | 2.13.0+cu129 |
| Transformers | 5.15.0 |
| CUDA runtime / NVIDIA 驱动 | 12.9 / 550.127.08 |
| GPU | 单张 NVIDIA H100 80GB HBM3 |
| 音频依赖 | av 18.1.0、soundfile 0.14.0、scipy 1.18.0、soxr 1.1.0、NumPy 2.3.5 |
| Hub 工具 / HTTP 客户端 | huggingface_hub 1.27.0 / requests 2.34.2 |
| 服务配置 | FP32、eager、GPU memory utilization 0.40、实际 max model length 40,960 |
这是**既有环境验证,不是全新安装验证**。没有安装、升级或重新解析依赖。
以上版本是环境观测结果,不是经全新安装验证的 lockfile。
不能据此宣称浮动 `pip install vllm` 或直接加载浮动 Hub 模型 ID 可复现本次结果。
[vLLM #54944](https://github.com/vllm-project/vllm/pull/54944) 于 2026-09-05
合并,merge commit 为 `e473e9036f979d546830aece9855027049faf0ba`。
它更新 supported-model 文档及测试 registry 的官方 checkpoint 引用,
没有修改推理实现。2026-09-07 审计时 main 已使用官方引用,
但 v0.28.0 仍引用社区产物。**已合并不等于已发布**。
本次也没有验证 main 或 v0.28.0,更不覆盖上游测试 registry 独立的
Transformers 版本约束。
## 准备固定的本地快照
以下命令将实际验证流程中的私有绝对路径替换成可移植变量。
请把 `VLLM_PYTHON` 指向已准备好且符合上表的环境;
示例 `.venv` 路径不代表本次新建过虚拟环境。
使用全新的隔离验证目录并保留下载 manifest。
可使用已有 Hub 认证,不要输出凭据。
```sh
export VLLM_PYTHON="$PWD/.venv/bin/python"
export VALIDATION_DIR="$PWD/.official-native-validation"
export MODEL_DIR="$VALIDATION_DIR/official-model/a4362c943d48951f98ca2a62181cc028970270c5"
```
在与实测相同的 native-import 上下文检查版本:
```sh
"$VLLM_PYTHON" - <<'PY'
import importlib.metadata as metadata
import torch
import vllm.model_executor.models.funasr
for name, expected in {"vllm": "0.27.1+cu129", "torch": "2.13.0+cu129",
"transformers": "5.15.0"}.items():
assert metadata.version(name) == expected, (name, metadata.version(name))
assert torch.version.cuda == "12.9" and torch.cuda.is_available()
PY
```
按不可变 revision 下载全部 23 个文件,先校验 Hub 元数据再启动。
此脚本不执行下载内容中的转换脚本,也不修改快照:
```sh
HF_HUB_DISABLE_TELEMETRY=1 HF_XET_CACHE="$VALIDATION_DIR/isolated-xet-cache" "$VLLM_PYTHON" - <<'PY'
import hashlib
import json
import os
from pathlib import Path
from huggingface_hub import HfApi, snapshot_download
model_id = "FunAudioLLM/Fun-ASR-Nano-2512-vllm"
revision = "a4362c943d48951f98ca2a62181cc028970270c5"
root = Path(os.environ["MODEL_DIR"])
info = HfApi().model_info(model_id, revision=revision, files_metadata=True)
assert info.sha == revision and len(info.siblings) == 23
snapshot_download(model_id, revision=revision, local_dir=str(root),
cache_dir=str(Path(os.environ["VALIDATION_DIR"]) / "isolated-hf-cache"),
max_workers=4)
files = []
for item in info.siblings:
content = (root / item.rfilename).read_bytes()
digest = hashlib.sha256(content).hexdigest()
assert len(content) == item.size, item.rfilename
if item.lfs:
assert digest == item.lfs.sha256, item.rfilename
else:
assert hashlib.sha1(f"blob {len(content)}\0".encode() + content).hexdigest() == item.blob_id
files.append({"path": item.rfilename, "size": len(content), "sha256": digest})
assert json.loads((root / "config.json").read_text())["architectures"] == ["FunASRForConditionalGeneration"]
(root.parent / "download-manifest.json").write_text(json.dumps(files, indent=2) + "\n")
PY
```
### Checkpoint 与音频摘要
音频均为单声道 48 kHz MP3。ffprobe 测得容器时长分别为
**中文 5.616 s、英文 7.176 s、日文 7.224 s**。
服务 usage 计数将其向上取整为 6/8/8 秒;取整值不是文件实测时长。
| 文件 | 字节数 | SHA256 |
| --- | --- | --- |
| `example/en.mp3` | 57441 | `f10378336a4e584f3f63799e62f99d5add3c2a401b51d3abe7d3a3a82f255ada` |
| `example/ja.mp3` | 57837 | `496dbc43b289e1d0d0cb916df9737450bca56acd8aaca046a7a2472363b1be53` |
| `example/zh.mp3` | 44973 | `0e64de19e4ff9a02e682955c9112f32d2317cfdbb5bc2f3504664044c993f195` |
| `model.safetensors` | 1970899072 | `96dfbec48282dd24d3334369a01e9e909f321ee39a1b0003c528c5379f68c1a6` |
[可复现元数据](benchmark/vllm_official_native_20260907.json) 包含全部
23 个文件的大小、SHA256、上游 Git/LFS 摘要、包版本、精确 HTTP 字段、
原始响应摘要及未取整耗时。公开内容省略私有主机路径、GPU 标识和凭据。
其中原始 `/v1/models` 响应的本地 root 保留在私有证据中;
其摘要对应原始字节,不是脱敏后的替代响应。
## 离线启动与 loopback
先确认 GPU 0 空闲、loopback 端口 57185 未被占用。如需换端口,
须同步更改所有请求;本记录只使用了 57185。在准备环境的 shell 中以前台启动:
```sh
CUDA_VISIBLE_DEVICES=0 PYTHONDONTWRITEBYTECODE=1 \
HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 \
HF_HUB_DISABLE_IMPLICIT_TOKEN=1 HF_HUB_DISABLE_TELEMETRY=1 VLLM_NO_USAGE_STATS=1 \
VLLM_CACHE_ROOT="$VALIDATION_DIR/runtime-cache" TRITON_CACHE_DIR="$VALIDATION_DIR/triton-cache" \
"$VLLM_PYTHON" -B -m vllm.entrypoints.openai.api_server \
--model "$MODEL_DIR" --served-model-name fun-asr-nano-official-a4362c94 \
--host 127.0.0.1 --port 57185 --dtype float32 \
--gpu-memory-utilization 0.40 --enforce-eager
```
实际服务加载经过验证的**本地快照**,Hub 与 Transformers 均为离线模式。
没有使用 `--trust-remote-code`,也没有让服务从浮动模型 ID 加载。
下载完成后,启动至健康状态耗时 84.123246 s;这不是启动性能保证。
在第二个 shell 中将 `VALIDATION_DIR` 和 `MODEL_DIR` 设为相同绝对路径,
然后等待健康检查成功:
```sh
curl --max-time 15 -fsS http://127.0.0.1:57185/health
curl --max-time 15 -fsS http://127.0.0.1:57185/v1/models
```
`/v1/models` 应包含 ID `fun-asr-nano-official-a4362c94`,其 root
应等于展开后的 `MODEL_DIR`。实测同时检查了二者。
## 实际转写请求
实测 harness 使用 Python requests,multipart 音频 MIME 为 `audio/mpeg`,
连接/读取超时为 5/45 s,显式传入 `language=zh`、`language=en`、
`language=ja` 和 `response_format=json`,没有覆盖 temperature 或生成长度。
以下等价 curl 保留实际请求字段,但没有单独测量 curl 耗时:
```sh
curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F "file=@$MODEL_DIR/example/zh.mp3;type=audio/mpeg" -F model=fun-asr-nano-official-a4362c94 -F language=zh -F response_format=json
curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F "file=@$MODEL_DIR/example/en.mp3;type=audio/mpeg" -F model=fun-asr-nano-official-a4362c94 -F language=en -F response_format=json
curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F "file=@$MODEL_DIR/example/ja.mp3;type=audio/mpeg" -F model=fun-asr-nano-official-a4362c94 -F language=ja -F response_format=json
curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F "file=@$MODEL_DIR/example/zh.mp3;type=audio/mpeg" -F model=fun-asr-nano-official-a4362c94 -F language=zh -F 'hotwords=开放时间,开放时间,开放时间' -F response_format=json
```
实际返回文本:
- 中文基线:开饭时间早上九点至下午五点。
- 英文:The tribal chieftain called for the boy, and presented him with fifty pieces of gold.
- 日文:うちの中学は弁当制で、持っていけない場合は、五十円の学校販売のパンを買う。
- 中文加 `hotwords=开放时间,开放时间,开放时间`:开放时间早上九点至下午五点。
保留基线误识别结果。重复热词只证明此样本输出发生改变,
不能直接推广为通用热词策略或准确率保证。
| 已记录请求 | HTTP | 客户端 wall time(秒) |
| --- | --- | --- |
| GET /health | 200 | 0.001023 |
| GET /v1/models | 200 | 0.002583 |
| POST zh (first) | 200 | 0.889547 |
| POST en | 200 | 0.386346 |
| POST ja | 200 | 0.473937 |
| POST zh + hotwords | 200 | 0.190610 |
| Concurrent en | 200 | 0.799900 |
| Concurrent ja | 200 | 0.904910 |
首次中文请求**未经转写预热**:它是健康检查和模型列表之后的第一个转写请求。
后续请求复用同一引擎。时间包含本地 HTTP 与解码,不含模型下载和服务启动;
每项只有一次观测,不是延迟分布。
四个顺序转写请求之后,双 worker 的 `ThreadPoolExecutor` 同时发出相同的
英文和日文 multipart 请求,均返回 200,文本与顺序请求一致。
两请求总 wall time 为 **0.9112209342420101 s(0.911 s)**,
包含 executor 建立与等待两项完成。这只是**两个请求的并发功能探针**,
不是吞吐量、生产容量或准确率研究,也不是与社区历史 1.123 s 探针的性能对比。
## Harness 边界与清理
首次 harness 尝试在**创建服务进程之前**被包清单 guard 拦截。
准备阶段导入 native vLLM,使 setuptools 的 vendored 包进入 `sys.path`,
原始 serve 阶段却使用不同导入上下文比较包清单。
统一导入上下文后差异为空,原始失败和修正均已保留。
早期失败状态字段曾误写为服务已启动,进程证据与修正记录确认当时没有启动。
没有修改依赖或模型代码;之后首次实际启动服务即完成 8 请求 smoke。
有时限的 harness 终止了自己创建的进程组,并等待服务及子进程退出。
服务退出码为 0、端口关闭、GPU 无计算进程,包与 native 源文件摘要不变。
独立检查再次确认全部 23 个原始文件及备份摘要、8 个原始响应、模型身份和清理状态。
手工复现后请停止前台服务,确认其 worker 与端口均已退出,不要终止无关 GPU 任务。
## 部署边界
这里只覆盖 request/response `/v1/audio/transcriptions`,没有验证
`/v1/realtime` 实时流式会话。长音频、说话人分离、时间戳准确率、
其他 GPU、持续负载与生产容量均不在范围内。
不能由原生服务推导 FunASR SDK 或包发布已通过验证。
worker 保持绑定 `127.0.0.1`。对外提供 API 前,由网关完成认证、
TLS、限流、音频大小/时长限制,隔离上传文件并落实保留策略。
网关不在本次 smoke 范围内。参见[部署矩阵](deployment_matrix_zh.md)和
[服务安全边界](../examples/openai_api/SECURITY_zh.md)。
@@ -1024,6 +1024,7 @@ td a {
.limitation-callout h2 {
max-width: 940px;
margin: 0;
overflow-wrap: anywhere;
font-size: 28px;
line-height: 1.35;
}
@@ -1033,6 +1034,14 @@ td a {
color: var(--muted);
}
.limitation-summary {
max-width: 78ch;
margin: 14px 0 0;
overflow-wrap: anywhere;
font-size: 16px;
line-height: 1.65;
}
.evidence-list {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px 28px;
+124 -35
View File
@@ -191,40 +191,89 @@
"workloads": ["batch", "private-api"],
"hardware": ["nvidia-gpu", "kubernetes"],
"priorities": ["throughput", "latency"],
"models": ["Fun-ASR-Nano-2512 (community vLLM conversion)"],
"models": [
"FunAudioLLM/Fun-ASR-Nano-2512-vllm (official checkpoint)"
],
"operating_systems": ["Linux"],
"interfaces": ["OpenAI-compatible HTTP"],
"tested": {"funasr": "Fun-ASR-Nano-2512 conversion@e718b36e", "runtime": "vLLM 0.27.1+cu129 / Torch 2.13.0+cu129", "verified": "2026-08-13"},
"tested": {
"funasr": "Official model revision a4362c943d48951f98ca2a62181cc028970270c5; not a FunASR package version",
"runtime": "vLLM 0.27.1+cu129 / Torch 2.13.0+cu129 / Transformers 5.15.0 / CUDA 12.9 / Python 3.12.3",
"verified": "2026-09-07"
},
"commands": {
"install": [
"curl -fL https://github.com/vllm-project/vllm/releases/download/v0.27.1/vllm-0.27.1%2Bcu129-cp38-abi3-manylinux_2_28_x86_64.whl -o vllm-0.27.1+cu129-cp38-abi3-manylinux_2_28_x86_64.whl",
"echo \"bf0d52faa2a51e7a01c6856a7a8a2d1307fd0ff711415d34168a67ffac0fa47b vllm-0.27.1+cu129-cp38-abi3-manylinux_2_28_x86_64.whl\" | sha256sum -c -",
"uv venv --python 3.12 .venv && uv pip install --python .venv/bin/python --torch-backend=auto \"vllm[audio] @ file://$PWD/vllm-0.27.1+cu129-cp38-abi3-manylinux_2_28_x86_64.whl\""
"export VLLM_PYTHON=\"$PWD/.venv/bin/python\"\nexport VALIDATION_DIR=\"$PWD/.official-native-validation\"\nexport MODEL_DIR=\"$VALIDATION_DIR/official-model/a4362c943d48951f98ca2a62181cc028970270c5\"",
"\"$VLLM_PYTHON\" - <<'PY'\nimport importlib.metadata as metadata\nimport torch\nimport vllm.model_executor.models.funasr\n\nfor name, expected in {\"vllm\": \"0.27.1+cu129\", \"torch\": \"2.13.0+cu129\",\n \"transformers\": \"5.15.0\"}.items():\n assert metadata.version(name) == expected, (name, metadata.version(name))\nassert torch.version.cuda == \"12.9\" and torch.cuda.is_available()\nPY",
"HF_HUB_DISABLE_TELEMETRY=1 HF_XET_CACHE=\"$VALIDATION_DIR/isolated-xet-cache\" \"$VLLM_PYTHON\" - <<'PY'\nimport hashlib\nimport json\nimport os\nfrom pathlib import Path\nfrom huggingface_hub import HfApi, snapshot_download\n\nmodel_id = \"FunAudioLLM/Fun-ASR-Nano-2512-vllm\"\nrevision = \"a4362c943d48951f98ca2a62181cc028970270c5\"\nroot = Path(os.environ[\"MODEL_DIR\"])\ninfo = HfApi().model_info(model_id, revision=revision, files_metadata=True)\nassert info.sha == revision and len(info.siblings) == 23\nsnapshot_download(model_id, revision=revision, local_dir=str(root),\n cache_dir=str(Path(os.environ[\"VALIDATION_DIR\"]) / \"isolated-hf-cache\"),\n max_workers=4)\nfiles = []\nfor item in info.siblings:\n content = (root / item.rfilename).read_bytes()\n digest = hashlib.sha256(content).hexdigest()\n assert len(content) == item.size, item.rfilename\n if item.lfs:\n assert digest == item.lfs.sha256, item.rfilename\n else:\n assert hashlib.sha1(f\"blob {len(content)}\\0\".encode() + content).hexdigest() == item.blob_id\n files.append({\"path\": item.rfilename, \"size\": len(content), \"sha256\": digest})\nassert json.loads((root / \"config.json\").read_text())[\"architectures\"] == [\"FunASRForConditionalGeneration\"]\n(root.parent / \"download-manifest.json\").write_text(json.dumps(files, indent=2) + \"\\n\")\nPY"
],
"launch": [
"CUDA_VISIBLE_DEVICES=0 .venv/bin/vllm serve allendou/Fun-ASR-Nano-2512-vllm --revision e718b36e2578203ec893e9b488239225f8d668e2 --served-model-name fun-asr-nano --host 127.0.0.1 --port 8899 --dtype float32 --gpu-memory-utilization 0.40 --enforce-eager"
"CUDA_VISIBLE_DEVICES=0 PYTHONDONTWRITEBYTECODE=1 \\\nHF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 \\\nHF_HUB_DISABLE_IMPLICIT_TOKEN=1 HF_HUB_DISABLE_TELEMETRY=1 VLLM_NO_USAGE_STATS=1 \\\nVLLM_CACHE_ROOT=\"$VALIDATION_DIR/runtime-cache\" TRITON_CACHE_DIR=\"$VALIDATION_DIR/triton-cache\" \\\n\"$VLLM_PYTHON\" -B -m vllm.entrypoints.openai.api_server \\\n --model \"$MODEL_DIR\" --served-model-name fun-asr-nano-official-a4362c94 \\\n --host 127.0.0.1 --port 57185 --dtype float32 \\\n --gpu-memory-utilization 0.40 --enforce-eager"
],
"health": [
"curl -fsS http://127.0.0.1:8899/health",
"curl -fsS http://127.0.0.1:8899/v1/models"
"curl --max-time 15 -fsS http://127.0.0.1:57185/health",
"curl --max-time 15 -fsS http://127.0.0.1:57185/v1/models"
],
"smoke": [
"curl -fL https://huggingface.co/allendou/Fun-ASR-Nano-2512-vllm/resolve/e718b36e2578203ec893e9b488239225f8d668e2/example/zh.mp3 -o zh.mp3 && echo \"0e64de19e4ff9a02e682955c9112f32d2317cfdbb5bc2f3504664044c993f195 zh.mp3\" | sha256sum -c -",
"curl -fsS http://127.0.0.1:8899/v1/audio/transcriptions -F file=@zh.mp3 -F model=fun-asr-nano -F language=zh -F response_format=json",
"curl -fsS http://127.0.0.1:8899/v1/audio/transcriptions -F file=@zh.mp3 -F model=fun-asr-nano -F language=zh -F 'hotwords=开放时间,开放时间,开放时间' -F response_format=json"
"curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F \"file=@$MODEL_DIR/example/zh.mp3;type=audio/mpeg\" -F model=fun-asr-nano-official-a4362c94 -F language=zh -F response_format=json",
"curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F \"file=@$MODEL_DIR/example/en.mp3;type=audio/mpeg\" -F model=fun-asr-nano-official-a4362c94 -F language=en -F response_format=json",
"curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F \"file=@$MODEL_DIR/example/ja.mp3;type=audio/mpeg\" -F model=fun-asr-nano-official-a4362c94 -F language=ja -F response_format=json",
"curl --max-time 45 -fsS http://127.0.0.1:57185/v1/audio/transcriptions -F \"file=@$MODEL_DIR/example/zh.mp3;type=audio/mpeg\" -F model=fun-asr-nano-official-a4362c94 -F language=zh -F 'hotwords=开放时间,开放时间,开放时间' -F response_format=json"
]
},
"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"},
{"label": "FunASR initialization fix", "url": "https://github.com/vllm-project/vllm/pull/44215"},
{"label": "validated community conversion at e718b36e", "url": "https://huggingface.co/allendou/Fun-ASR-Nano-2512-vllm/tree/e718b36e2578203ec893e9b488239225f8d668e2"},
{"label": "official FunASR split-engine guide", "url": "https://github.com/modelscope/FunASR/blob/main/docs/vllm_guide.md"},
{"label": "FunASR deployment matrix", "url": "https://github.com/modelscope/FunASR/blob/main/docs/deployment_matrix.md"}
{
"label": "2026-09-07 official checkpoint native HTTP functional validation",
"url": "https://github.com/modelscope/FunASR/blob/main/docs/vllm_official_native_validation.md"
},
{
"label": "2026-09-07 官方 checkpoint 原生 HTTP 功能验证(中文)",
"url": "https://github.com/modelscope/FunASR/blob/main/docs/vllm_official_native_validation_zh.md"
},
{
"label": "23-file manifest and 8 HTTP response digests (private paths redacted)",
"url": "https://github.com/modelscope/FunASR/blob/main/docs/benchmark/vllm_official_native_20260907.json"
},
{
"label": "2026-08-13 historical community checkpoint validation (unchanged)",
"url": "https://github.com/modelscope/FunASR/blob/main/docs/vllm_native_funasr_validation.md"
},
{
"label": "Official reference in upstream docs/test registry; merged main, not a release claim (#54944)",
"url": "https://github.com/vllm-project/vllm/pull/54944"
},
{
"label": "Official checkpoint at immutable a4362c94 (validated separately on 2026-09-07)",
"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"
},
{
"label": "FunASR initialization fix",
"url": "https://github.com/vllm-project/vllm/pull/44215"
},
{
"label": "validated community conversion at e718b36e",
"url": "https://huggingface.co/allendou/Fun-ASR-Nano-2512-vllm/tree/e718b36e2578203ec893e9b488239225f8d668e2"
},
{
"label": "official FunASR split-engine guide",
"url": "https://github.com/modelscope/FunASR/blob/main/docs/vllm_guide.md"
},
{
"label": "FunASR deployment matrix",
"url": "https://github.com/modelscope/FunASR/blob/main/docs/deployment_matrix.md"
}
],
"benchmarks": [
{
@@ -244,27 +293,67 @@
"translations": {
"zh": {
"name": "vLLM 原生 FunASR 服务",
"summary": "通过 vLLM 内置的 FunASR 架构,在 NVIDIA GPU 上提供兼容 OpenAI 的多语言转写与热词接口。",
"fit": ["需要标准 /v1/audio/transcriptions 接口", "Fun-ASR-Nano 批量或并发转写", "已有 NVIDIA GPU 和 Linux 服务环境"],
"not_fit": ["未经重新验证就直接替换下方固定版本 checkpoint", "纯 CPU 或边缘设备", "尚未完成显存、并发与业务音频压测的公网服务"],
"summary": "使用官方 FunAudioLLM/Fun-ASR-Nano-2512-vllm 固定快照,在 vLLM 0.27.1 上提供中英日转写与热词接口;已通过单张 H100 的独立功能验证。",
"fit": [
"需要标准 /v1/audio/transcriptions 接口",
"Fun-ASR-Nano 批量或并发转写",
"已有 NVIDIA GPU 和 Linux 服务环境"
],
"not_fit": [
"未经重新验证就直接替换下方固定版本 checkpoint",
"纯 CPU 或边缘设备",
"尚未完成显存、并发与业务音频压测的公网服务"
],
"selection_reason": "vLLM 已原生注册 FunASR 架构,并提供 OpenAI 兼容转写、显存调度、并发和热词参数。",
"primary_limitation": "下方历史实测与命令使用社区转换 checkpoint。官方 FunAudioLLM/Fun-ASR-Nano-2512-vllm 已被 vLLM 上游登记(PR #54944);这不等于本页旧测试已覆盖新权重,切换时须重新验证。历史数据仅覆盖单张 H100,目标 GPU、音频、语言、热词与流量均须复测。",
"primary_limitation": "当前命令对应官方 a4362c943d48951f98ca2a62181cc028970270c5,2026-09-07 在既有 vLLM 0.27.1+cu129 / Torch 2.13.0+cu129 / Transformers 5.15.0 / CUDA 12.9 环境验证,不是全新安装验证。准备步骤只检查版本和下载本地快照,不重新安装依赖。8 个 HTTP 请求均为功能验证;首次中文未预热,两请求 0.911 s 不代表容量。下方 2026-08-13 benchmark 仍是社区 checkpoint 的历史数据,没有重命名或与新结果比较。#54944 仅更新 main 的文档/测试 registry;审计时 v0.28.0 仍引用社区产物,不代表官方引用已发布。",
"status_label": "社区验证",
"operations": ["固定 vLLM wheel、Torch、CUDA、模型 revision 与音频依赖", "等待 /health 就绪并完成预热后再采集容量数据", "记录队列等待、最终结果延迟、显存和热词命中率"],
"security": ["在 API 网关实现认证、TLS、限流和音频大小/时长限制", "worker 仅绑定内网地址,不直接暴露公网", "校验 wheel、模型 revision 和示例音频摘要,隔离模型缓存与上传临时目录"],
"troubleshooting": ["音频返回 400 时确认安装了 vllm[audio]", "启动时 KV cache 不足则提高显存利用率或在业务允许时降低 max-model-len", "非英文请求必须显式传 language;热词用逗号分隔并以真实业务样本验证偏置强度"]
"operations": [
"固定官方模型 revision 与本地文件摘要,以及 vLLM 0.27.1+cu129、Torch 2.13.0+cu129、Transformers 5.15.0、CUDA 12.9;不要将模型 revision 当成 FunASR 包版本",
"等待 /health 就绪并完成预热后再采集容量数据",
"记录队列等待、最终结果延迟、显存和热词命中率"
],
"security": [
"在 API 网关实现认证、TLS、限流和音频大小/时长限制",
"worker 仅绑定 127.0.0.1;网关未在本次 smoke 中验证,不直接暴露公网",
"校验 wheel、模型 revision 和示例音频摘要,隔离模型缓存与上传临时目录"
],
"troubleshooting": [
"音频返回 400 时确认安装了 vllm[audio]",
"启动时 KV cache 不足则提高显存利用率或在业务允许时降低 max-model-len",
"非英文请求必须显式传 language;热词用逗号分隔并以真实业务样本验证偏置强度"
]
},
"en": {
"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": ["Replacing the pinned checkpoint below without revalidation", "CPU-only or edge devices", "Internet-facing service before memory, concurrency, and workload tests"],
"summary": "Serve the pinned official FunAudioLLM/Fun-ASR-Nano-2512-vllm snapshot on vLLM 0.27.1; Chinese, English, Japanese and hotwords passed a separate single-H100 functional smoke.",
"fit": [
"A standard /v1/audio/transcriptions endpoint",
"Concurrent or batch Fun-ASR-Nano transcription",
"An NVIDIA GPU Linux serving environment"
],
"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 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.",
"primary_limitation": "Current commands use official FunAudioLLM/Fun-ASR-Nano-2512-vllm@a4362c943d48951f98ca2a62181cc028970270c5, tested 2026-09-07 in an existing environment: vLLM 0.27.1+cu129 / Torch 2.13.0+cu129 / Transformers 5.15.0 / CUDA 12.9. This is not a clean installation validation; preparation only checks versions and downloads a local snapshot. Eight HTTP requests establish functional compatibility only; first Chinese was not warmed, and the 0.911 s two-request probe is not capacity evidence. The 2026-08-13 benchmark below remains a historical community-converted checkpoint result, neither relabeled nor compared with the new run. #54944 updates main docs/test registry only; v0.28.0 still referenced the community artifact at audit time, not a claim of released official references.",
"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"],
"troubleshooting": ["For audio HTTP 400 responses, confirm vllm[audio] is installed", "If startup reports insufficient KV cache, raise GPU utilization or lower max-model-len only when the workload permits", "Pass language explicitly for non-English audio; comma-separate hotwords and validate bias strength on production samples"]
"operations": [
"Pin the official model revision and local file digests plus vLLM 0.27.1+cu129, Torch 2.13.0+cu129, Transformers 5.15.0 and CUDA 12.9; a model revision is not a FunASR package version",
"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 the worker to 127.0.0.1; the gateway was not validated by this smoke, and the worker must not be exposed directly",
"Verify wheel, model revision, and sample digests; isolate model cache and temporary uploads"
],
"troubleshooting": [
"For audio HTTP 400 responses, confirm vllm[audio] is installed",
"If startup reports insufficient KV cache, raise GPU utilization or lower max-model-len only when the workload permits",
"Pass language explicitly for non-English audio; comma-separate hotwords and validate bias strength on production samples"
]
}
}
},
@@ -25,7 +25,8 @@
{"slug": "kubernetes", "group": "deploy", "zh": "Kubernetes 部署", "en": "Kubernetes deployment", "source_zh": "examples/openai_api/kubernetes/README_zh.md", "source_en": "examples/openai_api/kubernetes/README.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": "official-native-vllm", "group": "deploy", "zh": "官方原生 vLLM 功能验证(2026-09-07)", "en": "Official native vLLM functional validation (2026-09-07)", "source_zh": "docs/vllm_official_native_validation_zh.md", "source_en": "docs/vllm_official_native_validation.md"},
{"slug": "native-vllm", "group": "deploy", "zh": "社区原生 vLLM 历史记录(2026-08-13)", "en": "Historical community native vLLM record (2026-08-13)", "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": "security", "group": "operate", "zh": "服务安全边界", "en": "Service security boundaries", "source_zh": "examples/openai_api/SECURITY_zh.md", "source_en": "examples/openai_api/SECURITY.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"},
@@ -173,8 +173,8 @@
<section class="section content-section" data-section="limitations">
<div class="limitation-callout">
<p class="eyebrow">{{ '已知限制' if language == 'zh' else 'Known limitation' }}</p>
<h2>{{ copy.primary_limitation }}</h2>
<h2>{{ '已知限制' if language == 'zh' else 'Known limitations' }}</h2>
<p class="limitation-summary">{{ copy.primary_limitation }}</p>
<p>{{ '公开基准仅用于复现起点,不替代目标业务负载测试。' if language == 'zh' else 'Public benchmarks are reproduction starting points, not substitutes for target-workload testing.' }}</p>
</div>
</section>
@@ -362,6 +362,48 @@ for (const viewport of [
});
}
for (const language of ['zh', 'en']) {
test(`vLLM limitation preserves full revision and wraps on mobile (${language})`, async ({ page }, testInfo) => {
const revision = 'a4362c943d48951f98ca2a62181cc028970270c5';
await page.setViewportSize({ width: 390, height: 844 });
await page.goto(`${language === 'en' ? '/en' : ''}/deploy/vllm.html`);
await page.evaluate(() => document.fonts.ready);
const heading = page.locator('.limitation-callout h2');
await expect(heading).toHaveText(language === 'en' ? 'Known limitations' : '已知限制');
const summary = page.locator('.limitation-callout p.limitation-summary');
await expect(summary).toContainText(revision);
await summary.scrollIntoViewIfNeeded();
const layout = await summary.evaluate((node, revision) => {
const text = node.firstChild!;
const start = text.textContent!.indexOf(revision);
const range = document.createRange();
range.setStart(text, start);
range.setEnd(text, start + revision.length);
const bounds = node.getBoundingClientRect();
return {
overflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
summaryOverflow: node.scrollWidth - node.clientWidth,
summaryLeft: bounds.left,
summaryRight: bounds.right,
fontSize: Number.parseFloat(getComputedStyle(node).fontSize),
revisionRects: [...range.getClientRects()].map(rect => ({ left: rect.left, right: rect.right, top: rect.top })),
};
}, revision);
await testInfo.attach('limitation-layout', { body: JSON.stringify(layout), contentType: 'application/json' });
await page.screenshot({ path: testInfo.outputPath(`vllm-limitation-${language}-mobile.png`), fullPage: true });
await page.locator('.limitation-callout').screenshot({ path: testInfo.outputPath(`vllm-limitation-${language}-callout.png`) });
expect(layout.overflow).toBeLessThanOrEqual(1);
expect(layout.summaryOverflow).toBeLessThanOrEqual(1);
expect(layout.fontSize).toBeLessThanOrEqual(18);
expect(new Set(layout.revisionRects.map(rect => Math.round(rect.top))).size).toBeGreaterThan(1);
expect(layout.revisionRects.every(rect =>
rect.left >= layout.summaryLeft - 1 && rect.right <= layout.summaryRight + 1,
)).toBe(true);
});
}
test('reduced motion disables smooth scrolling', async ({ page }) => {
await page.emulateMedia({ reducedMotion: 'reduce' });
await page.goto('/en/');
@@ -31,6 +31,41 @@ def test_bilingual_docs_have_navigation_and_real_source_content(output):
assert moss.select_one('[data-source-link]')['href'].endswith('.md')
def test_official_native_record_has_local_language_links_and_search(output):
for language, prefix, suffix in (('zh', '', '_zh'), ('en', 'en/', '')):
route = '/' + prefix + 'docs/official-native-vllm.html'
page = BeautifulSoup((output / route.lstrip('/')).read_text(), 'html.parser')
assert page.select_one('[data-source-link]')['href'].endswith(
f'/docs/vllm_official_native_validation{suffix}.md')
links = {link['href'] for link in page.select('.docs-article a[href]')}
assert '/' + prefix + 'docs/native-vllm.html' in links
assert '/' + prefix + 'docs/deployment-matrix.html' in links
assert any('/docs/benchmark/vllm_official_native_20260907.json' in link for link in links)
rows = json.loads((output / f'search-{language}.json').read_text())
assert any(row['url'] == route and '2026-09-07' in row['title'] for row in rows)
deploy = BeautifulSoup((output / prefix / 'deploy/vllm.html').read_text(), 'html.parser')
commands = deploy.select_one('[data-section="commands"]').get_text()
assert 'FunAudioLLM/Fun-ASR-Nano-2512-vllm' in commands
assert 'allendou/' not in commands
def test_deployment_limitations_use_short_heading_and_preserve_full_body(output):
entries = json.loads((SITE_ROOT / 'data/deployments.json').read_text())['deployments']
for language in ('zh', 'en'):
for entry in entries:
route = entry['routes'][language].lstrip('/')
page = BeautifulSoup((output / route).read_text(), 'html.parser')
callout = page.select_one('[data-section="limitations"] .limitation-callout')
heading = callout.select_one('h2')
expected = entry['translations'][language]['primary_limitation']
assert heading.get_text() == ('已知限制' if language == 'zh' else 'Known limitations')
assert heading.get_text() != expected
summary = callout.select_one('p.limitation-summary')
assert summary is not None, (language, entry['id'])
assert summary.get_text() == expected, (language, entry['id'])
assert not callout.select('.eyebrow')
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())
@@ -0,0 +1,94 @@
"""Model-free contracts for the separate official native serving record."""
import hashlib
import json
from pathlib import Path
import ast
import subprocess
import pytest
SITE = Path(__file__).resolve().parents[1]
ROOT = SITE.parents[1]
REVISION = 'a4362c943d48951f98ca2a62181cc028970270c5'
MANIFEST = ROOT / 'docs/benchmark/vllm_official_native_20260907.json'
def test_dated_community_record_is_unchanged():
path = ROOT / 'docs/vllm_native_funasr_validation.md'
assert hashlib.sha256(path.read_bytes()).hexdigest() == (
'e8f348b0f473769e1ece224d881edcd03ce897e99bcd17918068ab97fbeb1583')
def test_official_catalogue_has_two_sources_and_keeps_historical_route():
pages = json.loads((SITE / 'data/documentation.json').read_text())['pages']
rows = [row for row in pages if row['slug'] == 'official-native-vllm']
assert len(rows) == 1
official = rows[0]
assert official['group'] == 'deploy'
for language, suffix in (('en', ''), ('zh', '_zh')):
path = f'docs/vllm_official_native_validation{suffix}.md'
assert official[f'source_{language}'] == path
assert Path(path).stem in (ROOT / 'docs/index.rst').read_text()
historical = next(row for row in pages if row['slug'] == 'native-vllm')
assert 'historical' in historical['en'].lower()
assert '历史' in historical['zh']
assert historical['source_en'] == historical['source_zh'] == 'docs/vllm_native_funasr_validation.md'
@pytest.mark.parametrize('suffix', ('', '_zh'))
def test_official_record_has_reproducibility_and_scoped_evidence(suffix):
path = ROOT / f'docs/vllm_official_native_validation{suffix}.md'
assert path.is_file(), 'Missing separate official source record'
text = path.read_text()
for marker in (REVISION, 'FunAudioLLM/Fun-ASR-Nano-2512-vllm',
'0.27.1+cu129', '2.13.0+cu129', '5.15.0', '12.9',
'HF_HUB_OFFLINE=1', 'TRANSFORMERS_OFFLINE=1',
'snapshot_download', '--model "$MODEL_DIR"',
'language=zh', 'language=en', 'language=ja',
'0.911', '5.616', '7.176', '7.224',
'vllm_native_funasr_validation.md',
'benchmark/vllm_official_native_20260907.json',
'54944', 'v0.28.0', '2026-08-13'):
assert marker in text
for private in ('/cpfs_speech/', '/Users/', 'ind-gpu8'):
assert private not in text
def test_official_manifest_preserves_exact_files_and_http_scope():
assert MANIFEST.is_file(), 'Missing public redacted reproducibility metadata'
record = json.loads(MANIFEST.read_text())
assert record['revision'] == REVISION
files = {row['path']: row for row in record['files']}
assert len(files) == len(record['files']) == 23
assert files['model.safetensors']['sha256'] == '96dfbec48282dd24d3334369a01e9e909f321ee39a1b0003c528c5379f68c1a6'
requests = record['http']['requests']
assert len(requests) == 8 and all(row['status'] == 200 for row in requests)
for row in requests:
if row['body_file'] != 'models.body':
assert hashlib.sha256(row['raw_body_utf8'].encode()).hexdigest() == row['response_sha256']
assert {row['request_fields']['language'] for row in requests if row['request_fields']} == {'zh', 'en', 'ja'}
assert record['http']['two_request_wall_seconds'] == 0.9112209342420101
assert record['first_chinese_request_warmed'] is False
assert record['clean_install_validated'] is False
assert record['guard_failure_before_server_spawn'] is True
assert record['cleanup']['all_owned_exited'] is True
for private in ('/cpfs_speech/', '/Users/', 'ind-gpu8', '/tmp/funasr-'):
assert private not in MANIFEST.read_text()
def test_portable_commands_match_both_sources_and_parse_without_execution():
entry = next(row for row in json.loads((SITE / 'data/deployments.json').read_text())['deployments']
if row['id'] == 'vllm')
record = json.loads(MANIFEST.read_text())
assert entry['commands'] == record['commands']
for commands in entry['commands'].values():
for command in commands:
result = subprocess.run(['bash', '-n'], input=command, text=True,
capture_output=True, timeout=10)
assert result.returncode == 0, result.stderr
if "<<'PY'\n" in command:
ast.parse(command.split("<<'PY'\n", 1)[1].rsplit('\nPY', 1)[0])
for suffix in ('', '_zh'):
text = (ROOT / f'docs/vllm_official_native_validation{suffix}.md').read_text()
assert command in text
+25 -12
View File
@@ -51,31 +51,41 @@ def test_vllm_contract_tracks_native_funasr_release_and_h100_validation(valid_re
assert entry['maturity'] == 'community-verified'
assert entry['tested'] == {
'funasr': 'Fun-ASR-Nano-2512 conversion@e718b36e',
'runtime': 'vLLM 0.27.1+cu129 / Torch 2.13.0+cu129',
'verified': '2026-08-13',
'funasr': 'Official model revision a4362c943d48951f98ca2a62181cc028970270c5; not a FunASR package version',
'runtime': 'vLLM 0.27.1+cu129 / Torch 2.13.0+cu129 / Transformers 5.15.0 / CUDA 12.9 / Python 3.12.3',
'verified': '2026-09-07',
}
assert entry['models'] == ['Fun-ASR-Nano-2512 (community vLLM conversion)']
assert entry['models'] == ['FunAudioLLM/Fun-ASR-Nano-2512-vllm (official checkpoint)']
install = '\n'.join(entry['commands']['install'])
assert 'vllm[audio]' in install
assert 'vllm-0.27.1%2Bcu129-cp38-abi3-manylinux_2_28_x86_64.whl' in install
assert 'bf0d52faa2a51e7a01c6856a7a8a2d1307fd0ff711415d34168a67ffac0fa47b' in install
for marker in ('snapshot_download', 'FunAudioLLM/Fun-ASR-Nano-2512-vllm',
'a4362c943d48951f98ca2a62181cc028970270c5', '5.15.0',
'2.13.0+cu129', '0.27.1+cu129', 'local_dir=', 'cache_dir='):
assert marker in install
assert 'pip install' not in install
assert 'uv venv' not in install
launch = '\n'.join(entry['commands']['launch'])
for marker in (
'vllm serve allendou/Fun-ASR-Nano-2512-vllm',
'--revision e718b36e2578203ec893e9b488239225f8d668e2',
'--served-model-name fun-asr-nano',
'-m vllm.entrypoints.openai.api_server',
'--model "$MODEL_DIR"',
'HF_HUB_OFFLINE=1', 'TRANSFORMERS_OFFLINE=1',
'--host 127.0.0.1', '--port 57185',
'--served-model-name fun-asr-nano-official-a4362c94',
'--dtype float32',
'--gpu-memory-utilization 0.40',
'--enforce-eager',
):
assert marker in launch
assert 'allendou' not in launch
smoke = '\n'.join(entry['commands']['smoke'])
assert '/v1/audio/transcriptions' in smoke
assert 'language=zh' in smoke
for language in ('zh', 'en', 'ja'):
assert f'language={language}' in smoke
assert 'hotwords=开放时间,开放时间,开放时间' in smoke
evidence_urls = {item['url'] for item in entry['evidence']}
for url in (
'https://github.com/modelscope/FunASR/blob/main/docs/vllm_native_funasr_validation.md',
'https://github.com/modelscope/FunASR/blob/main/docs/vllm_official_native_validation.md',
'https://github.com/modelscope/FunASR/blob/main/docs/vllm_official_native_validation_zh.md',
'https://github.com/vllm-project/vllm/releases/tag/v0.27.1',
'https://github.com/vllm-project/vllm/pull/33247',
'https://github.com/vllm-project/vllm/pull/39674',
@@ -94,7 +104,10 @@ 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 funaudiollm/fun-asr-nano-2512-vllm' in limitation
assert 'does not extend the old test evidence' in limitation
assert 'existing environment' in limitation
assert 'not a clean installation' in limitation
assert '2026-08-13' in limitation
assert 'v0.28.0' in limitation
assert 'https://github.com/vllm-project/vllm/pull/54944' in evidence_urls