mirror of
https://github.com/modelscope/FunASR.git
synced 2026-10-12 02:19:18 +00:00
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:
@@ -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."
|
||||
}
|
||||
@@ -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. |
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user