mirror of
https://github.com/modelscope/FunASR.git
synced 2026-10-12 02:19:18 +00:00
feat: serve MOSS diarization across deployment stack
Signed-off-by: LauraGPT <[email protected]>
This commit is contained in:
@@ -5,7 +5,13 @@ on:
|
||||
paths:
|
||||
- "funasr/models/moss_transcribe_diarize/**"
|
||||
- "funasr/auto/auto_model.py"
|
||||
- "funasr/bin/_server_app.py"
|
||||
- "funasr/bin/server.py"
|
||||
- "examples/openai_api/**"
|
||||
- "README*.md"
|
||||
- "docs/index.rst"
|
||||
- "docs/moss_transcribe_diarize*.md"
|
||||
- "web-pages/product-site/data/deployments.json"
|
||||
- "tests/test_moss_transcribe_diarize_*.py"
|
||||
- ".github/workflows/test-moss-adapter.yml"
|
||||
push:
|
||||
@@ -14,7 +20,13 @@ on:
|
||||
paths:
|
||||
- "funasr/models/moss_transcribe_diarize/**"
|
||||
- "funasr/auto/auto_model.py"
|
||||
- "funasr/bin/_server_app.py"
|
||||
- "funasr/bin/server.py"
|
||||
- "examples/openai_api/**"
|
||||
- "README*.md"
|
||||
- "docs/index.rst"
|
||||
- "docs/moss_transcribe_diarize*.md"
|
||||
- "web-pages/product-site/data/deployments.json"
|
||||
- "tests/test_moss_transcribe_diarize_*.py"
|
||||
- ".github/workflows/test-moss-adapter.yml"
|
||||
|
||||
@@ -45,9 +57,14 @@ jobs:
|
||||
funasr/models/moss_transcribe_diarize/model.py \
|
||||
tests/test_moss_transcribe_diarize_model.py \
|
||||
tests/test_moss_transcribe_diarize_docs.py
|
||||
python -m py_compile funasr/models/moss_transcribe_diarize/model.py
|
||||
python -m py_compile \
|
||||
funasr/models/moss_transcribe_diarize/model.py \
|
||||
funasr/bin/_server_app.py \
|
||||
funasr/bin/server.py \
|
||||
examples/openai_api/server.py
|
||||
- name: Run adapter and documentation contracts
|
||||
run: |
|
||||
python -m pytest -q \
|
||||
tests/test_moss_transcribe_diarize_model.py \
|
||||
tests/test_moss_transcribe_diarize_docs.py
|
||||
tests/test_moss_transcribe_diarize_docs.py \
|
||||
tests/test_server_app_openai_segments.py
|
||||
|
||||
@@ -251,8 +251,12 @@ pip install torch torchaudio
|
||||
pip install funasr vllm fastapi uvicorn python-multipart
|
||||
funasr-server --device cuda
|
||||
# → POST /v1/audio/transcriptions at localhost:8000
|
||||
# Joint long-form ASR + anonymous speaker labels (offline HTTP):
|
||||
funasr-server --model moss-transcribe-diarize --device cuda:0
|
||||
```
|
||||
|
||||
[MOSS service, Docker, Kubernetes, vLLM, SGLang, LocalAI, and FunClip guide →](./docs/moss_transcribe_diarize.md)
|
||||
|
||||
Verify it with a public sample:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -140,11 +140,15 @@ pip install funasr
|
||||
# OpenAI互換API(推奨)
|
||||
pip install funasr fastapi uvicorn python-multipart
|
||||
funasr-server --device cuda
|
||||
# オフライン長時間音声 ASR + 匿名 speaker label:
|
||||
funasr-server --model moss-transcribe-diarize --device cuda:0
|
||||
|
||||
# Dockerストリーミングサービス
|
||||
docker pull registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.12
|
||||
```
|
||||
|
||||
[MOSS service / Docker / Kubernetes / vLLM / SGLang / LocalAI / FunClip guide →](./docs/moss_transcribe_diarize.md)
|
||||
|
||||
CPU/エッジで Python なしのオフライン ASR が必要な場合は、llama.cpp / GGUF ランタイムを使えます:[funasr.com/deploy/llama-cpp](https://www.funasr.com/en/deploy/llama-cpp.html) · [Fun-ASR-Nano-GGUF](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-GGUF) · [SenseVoiceSmall-GGUF](https://huggingface.co/FunAudioLLM/SenseVoiceSmall-GGUF)。
|
||||
|
||||
[Colab quickstart →](./examples/colab/README_ja.md) · [OpenAI API example →](./examples/openai_api/README_ja.md) · [Client recipes →](./examples/openai_api/CLIENTS.md) · [Workflow recipes →](./examples/openai_api/WORKFLOWS.md) · [Postman collection →](./examples/openai_api/POSTMAN.md) · [OpenAPI spec →](./examples/openai_api/OPENAPI.md) · [Security guide →](./examples/openai_api/SECURITY.md) · [Deployment matrix →](./docs/deployment_matrix_ja.md) · [デプロイドキュメント →](./runtime/readme.md) · [Agent連携 →](https://modelscope.github.io/FunASR/agent.html)
|
||||
|
||||
@@ -140,11 +140,15 @@ pip install funasr
|
||||
# OpenAI 호환 API (권장)
|
||||
pip install funasr fastapi uvicorn python-multipart
|
||||
funasr-server --device cuda
|
||||
# 오프라인 장시간 ASR + 익명 speaker label:
|
||||
funasr-server --model moss-transcribe-diarize --device cuda:0
|
||||
|
||||
# Docker 스트리밍 서비스
|
||||
docker pull registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.12
|
||||
```
|
||||
|
||||
[MOSS service / Docker / Kubernetes / vLLM / SGLang / LocalAI / FunClip guide →](./docs/moss_transcribe_diarize.md)
|
||||
|
||||
CPU/엣지에서 Python 없이 오프라인 ASR만 필요하다면 llama.cpp / GGUF 런타임을 사용할 수 있습니다: [funasr.com/deploy/llama-cpp](https://www.funasr.com/en/deploy/llama-cpp.html) · [Fun-ASR-Nano-GGUF](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-GGUF) · [SenseVoiceSmall-GGUF](https://huggingface.co/FunAudioLLM/SenseVoiceSmall-GGUF).
|
||||
|
||||
[Colab quickstart →](./examples/colab/README_ko.md) · [OpenAI API example →](./examples/openai_api/README_ko.md) · [Client recipes →](./examples/openai_api/CLIENTS.md) · [Workflow recipes →](./examples/openai_api/WORKFLOWS.md) · [Postman collection →](./examples/openai_api/POSTMAN.md) · [OpenAPI spec →](./examples/openai_api/OPENAPI.md) · [Security guide →](./examples/openai_api/SECURITY.md) · [Deployment matrix →](./docs/deployment_matrix_ko.md) · [배포 문서 →](./runtime/readme.md) · [Agent 연동 →](https://modelscope.github.io/FunASR/agent.html)
|
||||
|
||||
@@ -275,8 +275,12 @@ funasr *.wav --output-format srt --output-dir ./output
|
||||
pip install funasr fastapi uvicorn python-multipart
|
||||
funasr-server --model sensevoice --device cuda
|
||||
# → POST /v1/audio/transcriptions,地址 localhost:8000
|
||||
# 离线长音频联合转写 + 匿名说话人标签:
|
||||
funasr-server --model moss-transcribe-diarize --device cuda:0
|
||||
```
|
||||
|
||||
[MOSS 服务、Docker、Kubernetes、vLLM、SGLang、LocalAI 与 FunClip 部署指南 →](./docs/moss_transcribe_diarize_zh.md)
|
||||
|
||||
使用公开样例音频验证服务:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -73,6 +73,8 @@ Overview
|
||||
|
||||
./deployment_matrix.md
|
||||
./deployment_matrix_zh.md
|
||||
./moss_transcribe_diarize.md
|
||||
./moss_transcribe_diarize_zh.md
|
||||
./runtime/readme.md
|
||||
./runtime/docs/SDK_tutorial_online.md
|
||||
./runtime/docs/SDK_tutorial.md
|
||||
|
||||
@@ -95,6 +95,44 @@ clients keep the exact tagged generation in `raw_text`; in structured mode,
|
||||
`raw_text` is the cleaned `text` returned by vLLM and the authoritative speaker
|
||||
metadata is in `sentence_info`.
|
||||
|
||||
## FunASR OpenAI-compatible service
|
||||
|
||||
The built-in offline HTTP service exposes the same normalized result through
|
||||
`/v1/audio/transcriptions`. It loads the pinned Transformers revision and does
|
||||
not attach an external VAD or speaker model:
|
||||
|
||||
```bash
|
||||
python -m pip install "transformers>=5.6,<6" fastapi uvicorn python-multipart
|
||||
funasr-server --model moss-transcribe-diarize --device cuda:0 --port 8000
|
||||
|
||||
curl -fsS http://127.0.0.1:8000/v1/audio/transcriptions \
|
||||
-F file=@meeting.wav \
|
||||
-F model=moss-transcribe-diarize \
|
||||
-F response_format=verbose_json
|
||||
```
|
||||
|
||||
The response contains `text`, audio `duration`, and `segments` with `start`,
|
||||
`end`, `text`, and anonymous `speaker` labels. The request does not need
|
||||
`spk=true`; if a generic client sends it, the service still uses MOSS's native
|
||||
labels and does not start a second diarization pipeline.
|
||||
|
||||
For a reproducible GPU container, build from the repository root with
|
||||
`examples/openai_api/docker-compose.moss.yml`. Kubernetes operators can build
|
||||
the same `funasr-moss-api:local` image and apply
|
||||
`examples/openai_api/kubernetes/funasr-moss-api.yaml`; replace the local image
|
||||
reference with the immutable digest from their registry before rollout.
|
||||
|
||||
MOSS is an offline long-form model, so it is not exposed by FunASR's realtime
|
||||
WebSocket service. Use the HTTP endpoint for complete files. FunClip consumes
|
||||
the same `sentence_info` contract for speaker-aware subtitles and clips.
|
||||
|
||||
The built-in service path was reproduced on one H100 80GB with Transformers
|
||||
`5.16.0.dev0` and Torch `2.11.0+cu130`. The pinned model processed the bundled
|
||||
6.000-second sample (`ea03e1f473ad1618a03da3327a545369cb8f6f06cb0f4115535e5a866167d47e`)
|
||||
through the real HTTP endpoint and returned one non-empty monotonic segment
|
||||
labelled `S01`, with `duration=6.0`. This is a functional service-contract
|
||||
smoke, not an accuracy, throughput, concurrency, or production-capacity claim.
|
||||
|
||||
## Choose a serving path
|
||||
|
||||
| Path | Environment | Response contract | Use when |
|
||||
|
||||
@@ -85,6 +85,41 @@ speaker `segments` 直接映射到 `sentence_info`。认证可通过 `vllm_api_k
|
||||
此时 `raw_text` 保留原始标签生成;结构化模式下,`raw_text` 是 vLLM 返回的清理后
|
||||
`text`,权威说话人信息位于 `sentence_info`。
|
||||
|
||||
## FunASR OpenAI 兼容服务
|
||||
|
||||
内置离线 HTTP 服务通过 `/v1/audio/transcriptions` 返回同一套标准化结果。
|
||||
服务加载固定的 Transformers revision,不会外挂 VAD 或第二套说话人模型:
|
||||
|
||||
```bash
|
||||
python -m pip install "transformers>=5.6,<6" fastapi uvicorn python-multipart
|
||||
funasr-server --model moss-transcribe-diarize --device cuda:0 --port 8000
|
||||
|
||||
curl -fsS http://127.0.0.1:8000/v1/audio/transcriptions \
|
||||
-F file=@meeting.wav \
|
||||
-F model=moss-transcribe-diarize \
|
||||
-F response_format=verbose_json
|
||||
```
|
||||
|
||||
响应包含 `text`、音频 `duration`,以及带 `start`、`end`、`text` 和匿名
|
||||
`speaker` 标签的 `segments`。请求不需要设置 `spk=true`;即使通用客户端传入
|
||||
该字段,服务也只使用 MOSS 原生标签,不会启动第二套说话人流水线。
|
||||
|
||||
GPU 容器可在仓库根目录使用
|
||||
`examples/openai_api/docker-compose.moss.yml` 构建。Kubernetes 运维可构建同一
|
||||
`funasr-moss-api:local` 镜像,再应用
|
||||
`examples/openai_api/kubernetes/funasr-moss-api.yaml`;上线前应将本地镜像名
|
||||
替换为内部镜像仓库的不可变 digest。
|
||||
|
||||
MOSS 是离线长音频模型,不接入 FunASR 的实时 WebSocket 服务;完整文件请使用
|
||||
HTTP 接口。FunClip 通过同一套 `sentence_info` 契约生成带说话人信息的字幕与
|
||||
片段。
|
||||
|
||||
内置服务路径已在一张 H100 80GB、Transformers `5.16.0.dev0`、Torch
|
||||
`2.11.0+cu130` 环境复现。固定 revision 的模型通过真实 HTTP 接口处理仓库自带
|
||||
6.000 秒样例(`ea03e1f473ad1618a03da3327a545369cb8f6f06cb0f4115535e5a866167d47e`),
|
||||
返回一个非空、时间单调且标记为 `S01` 的 segment,`duration=6.0`。这只是服务
|
||||
契约 smoke test,不代表准确率、吞吐、并发或生产容量。
|
||||
|
||||
## 选择服务后端
|
||||
|
||||
| 路径 | 环境 | 响应契约 | 适用场景 |
|
||||
|
||||
@@ -20,6 +20,10 @@ If the server is on another machine, replace `localhost` with the reachable host
|
||||
| `paraformer` | Mandarin production transcription | Includes VAD and punctuation. |
|
||||
| `paraformer-en` | English transcription | Smaller English-only route. |
|
||||
| `fun-asr-nano` | LLM-based ASR experiments | Pair with vLLM for higher throughput deployments. |
|
||||
| `moss-transcribe-diarize` | Offline multi-speaker long-form audio | Returns timestamps and anonymous speaker labels in `verbose_json`; do not add external VAD or diarization. |
|
||||
|
||||
For MOSS server, Docker, Kubernetes, vLLM, SGLang Omni, LocalAI, and FunClip
|
||||
paths, use the [dedicated deployment guide](../../docs/moss_transcribe_diarize.md).
|
||||
|
||||
## Python OpenAI SDK
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
FROM pytorch/pytorch:2.9.1-cuda12.8-cudnn9-runtime
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PIP_NO_CACHE_DIR=1 \
|
||||
FUNASR_MODEL=moss-transcribe-diarize \
|
||||
FUNASR_DEVICE=cuda:0 \
|
||||
FUNASR_PORT=8000
|
||||
|
||||
WORKDIR /opt/funasr
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ffmpeg git libsndfile1 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY . /opt/funasr
|
||||
|
||||
RUN python -m pip install --upgrade pip \
|
||||
&& python -m pip install . fastapi "uvicorn[standard]" python-multipart \
|
||||
&& python -m pip install "transformers>=5.6,<6"
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10m --retries=3 \
|
||||
CMD python -c "import os, urllib.request; urllib.request.urlopen('http://127.0.0.1:%s/health' % os.getenv('FUNASR_PORT', '8000'), timeout=3).read()" || exit 1
|
||||
|
||||
CMD ["sh", "-c", "funasr-server --host 0.0.0.0 --port ${FUNASR_PORT:-8000} --device ${FUNASR_DEVICE:-cuda:0} --model ${FUNASR_MODEL:-moss-transcribe-diarize}"]
|
||||
@@ -51,6 +51,10 @@ For OpenAI SDK clients, remember that SDK base URLs include `/v1`; this Gradio d
|
||||
| `paraformer` | Mandarin-oriented production transcription. |
|
||||
| `paraformer-en` | English-only compatibility checks. |
|
||||
| `fun-asr-nano` | LLM-based ASR and vLLM experiments. |
|
||||
| `moss-transcribe-diarize` | Offline long-form transcription with timestamps and anonymous speaker labels; select `verbose_json`. |
|
||||
|
||||
MOSS requires the dedicated GPU service environment and is not a realtime
|
||||
microphone model. See the [MOSS deployment guide](../../docs/moss_transcribe_diarize.md).
|
||||
|
||||
See the [model selection guide](../../docs/model_selection.md) for a deeper comparison.
|
||||
|
||||
|
||||
@@ -51,6 +51,10 @@ OpenAI SDK 的 base URL 需要包含 `/v1`;这个 Gradio demo 使用的是不
|
||||
| `paraformer` | 中文生产转写。 |
|
||||
| `paraformer-en` | 英文兼容性检查。 |
|
||||
| `fun-asr-nano` | LLM-based ASR 和 vLLM 实验。 |
|
||||
| `moss-transcribe-diarize` | 离线长音频转写、时间戳和匿名说话人标签;请选择 `verbose_json`。 |
|
||||
|
||||
MOSS 需要专用 GPU 服务环境,不适合作为实时麦克风模型。详见
|
||||
[MOSS 部署指南](../../docs/moss_transcribe_diarize_zh.md)。
|
||||
|
||||
更完整的对比见 [模型选择指南](../../docs/model_selection_zh.md)。
|
||||
|
||||
|
||||
@@ -11,6 +11,11 @@ cd examples/openai_api
|
||||
python server.py --model sensevoice --device cuda --port 8000
|
||||
```
|
||||
|
||||
For offline multi-speaker files, start the isolated MOSS service with
|
||||
`funasr-server --model moss-transcribe-diarize --device cuda:0`, set
|
||||
`FUNASR_MODEL=moss-transcribe-diarize`, and request `verbose_json`. See the
|
||||
[MOSS deployment guide](../../docs/moss_transcribe_diarize.md).
|
||||
|
||||
Then verify the service from another terminal:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -11,6 +11,11 @@ cd examples/openai_api
|
||||
python server.py --model sensevoice --device cuda --port 8000
|
||||
```
|
||||
|
||||
处理离线多人音频时,使用
|
||||
`funasr-server --model moss-transcribe-diarize --device cuda:0` 启动独立服务,设置
|
||||
`FUNASR_MODEL=moss-transcribe-diarize` 并请求 `verbose_json`。详见
|
||||
[MOSS 部署指南](../../docs/moss_transcribe_diarize_zh.md)。
|
||||
|
||||
在另一个终端验证服务:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -38,7 +38,7 @@ Replace them with the URL reachable from your app, container, or workflow runtim
|
||||
| Field | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| `file` | binary | yes | Audio file such as wav, mp3, flac, m4a, ogg, or webm. |
|
||||
| `model` | string | no | Defaults to `sensevoice`; available aliases are listed by `/v1/models`. |
|
||||
| `model` | string | no | Defaults to `sensevoice`; `/v1/models` also lists `moss-transcribe-diarize` for offline long-form transcription with native anonymous speaker labels. |
|
||||
| `language` | string | no | Optional language hint. |
|
||||
| `response_format` | string | no | Use `json` or `verbose_json`. |
|
||||
|
||||
@@ -51,3 +51,7 @@ curl -fsS http://localhost:8000/openapi.json > /tmp/funasr-openapi-live.json
|
||||
```
|
||||
|
||||
The live FastAPI schema may include framework-specific validation details; this checked-in spec keeps the public integration surface small and stable.
|
||||
|
||||
MOSS requires its isolated Transformers environment. Start it with
|
||||
`funasr-server --model moss-transcribe-diarize --device cuda:0` and request
|
||||
`response_format=verbose_json`; see the [deployment guide](../../docs/moss_transcribe_diarize.md).
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
| Field | Type | Required | 说明 |
|
||||
|---|---|---|---|
|
||||
| `file` | binary | yes | 音频文件,例如 wav、mp3、flac、m4a、ogg 或 webm。 |
|
||||
| `model` | string | no | 默认 `sensevoice`;可用别名由 `/v1/models` 返回。 |
|
||||
| `model` | string | no | 默认 `sensevoice`;`/v1/models` 也会列出用于离线长音频与原生匿名说话人标签的 `moss-transcribe-diarize`。 |
|
||||
| `language` | string | no | 可选语言提示。 |
|
||||
| `response_format` | string | no | 使用 `json` 或 `verbose_json`。 |
|
||||
|
||||
@@ -51,3 +51,7 @@ curl -fsS http://localhost:8000/openapi.json > /tmp/funasr-openapi-live.json
|
||||
```
|
||||
|
||||
实时 FastAPI schema 可能包含框架级校验细节;仓库中的静态规范保留更小、更稳定的公开集成面。
|
||||
|
||||
MOSS 需要独立 Transformers 环境。使用
|
||||
`funasr-server --model moss-transcribe-diarize --device cuda:0` 启动,并请求
|
||||
`response_format=verbose_json`;详见 [部署指南](../../docs/moss_transcribe_diarize_zh.md)。
|
||||
|
||||
@@ -92,6 +92,12 @@ curl http://localhost:8000/v1/audio/transcriptions \
|
||||
| `paraformer` | 120x realtime | 15x realtime | zh/en | Punctuation |
|
||||
| `paraformer-en` | 120x realtime | 15x realtime | en | English only |
|
||||
| `fun-asr-nano` | 17x realtime | 3.6x realtime | zh/en/ja + Chinese dialects/accents | LLM-based, timestamps |
|
||||
| `moss-transcribe-diarize` | Validate on target GPU | Offline only | Multilingual | Long-form timestamps + anonymous speaker labels |
|
||||
|
||||
MOSS uses a pinned third-party HF revision and must not be combined with an
|
||||
external VAD or speaker model. See the [complete MOSS deployment guide](../../docs/moss_transcribe_diarize.md)
|
||||
for `funasr-server`, Docker Compose, Kubernetes, vLLM, SGLang Omni, LocalAI,
|
||||
and FunClip paths.
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
@@ -89,6 +89,11 @@ curl http://localhost:8000/v1/audio/transcriptions \
|
||||
| `paraformer` | 120x realtime | 15x realtime | zh/en | 句読点復元 |
|
||||
| `paraformer-en` | 120x realtime | 15x realtime | en | 英語認識 |
|
||||
| `fun-asr-nano` | 17x realtime | 3.6x realtime | 中英日 + 中国語方言・地域アクセント | LLM-based、タイムスタンプ |
|
||||
| `moss-transcribe-diarize` | 対象 GPU で要検証 | オフラインのみ | 多言語 | 長時間 timestamp + 匿名 speaker label |
|
||||
|
||||
MOSS は固定 revision の third-party HF model を使用し、外部 VAD / speaker model
|
||||
とは併用しません。[MOSS deployment guide](../../docs/moss_transcribe_diarize.md) に
|
||||
`funasr-server`、Docker、Kubernetes、vLLM、SGLang、LocalAI、FunClip をまとめています。
|
||||
|
||||
## API エンドポイント
|
||||
|
||||
|
||||
@@ -89,6 +89,11 @@ curl http://localhost:8000/v1/audio/transcriptions \
|
||||
| `paraformer` | 120x realtime | 15x realtime | zh/en | 문장부호 복원 |
|
||||
| `paraformer-en` | 120x realtime | 15x realtime | en | 영어 인식 |
|
||||
| `fun-asr-nano` | 17x realtime | 3.6x realtime | 중영일 + 중국어 방언/지역 억양 | LLM-based, 타임스탬프 |
|
||||
| `moss-transcribe-diarize` | 대상 GPU 검증 필요 | 오프라인 전용 | 다국어 | 장시간 timestamp + 익명 speaker label |
|
||||
|
||||
MOSS는 고정 revision의 third-party HF model을 사용하며 외부 VAD / speaker model과
|
||||
결합하지 않습니다. `funasr-server`, Docker, Kubernetes, vLLM, SGLang, LocalAI,
|
||||
FunClip 경로는 [MOSS deployment guide](../../docs/moss_transcribe_diarize.md)를 참고하세요.
|
||||
|
||||
## API 엔드포인트
|
||||
|
||||
|
||||
@@ -89,6 +89,11 @@ curl http://localhost:8000/v1/audio/transcriptions \
|
||||
| `paraformer` | 120x realtime | 15x realtime | zh/en | 标点恢复 |
|
||||
| `paraformer-en` | 120x realtime | 15x realtime | en | 英文识别 |
|
||||
| `fun-asr-nano` | 17x realtime | 3.6x realtime | 中/英/日 + 中文方言/口音 | LLM-based,时间戳 |
|
||||
| `moss-transcribe-diarize` | 需在目标 GPU 验证 | 仅离线 | 多语言 | 长音频时间戳 + 匿名说话人标签 |
|
||||
|
||||
MOSS 使用固定 revision 的第三方 HF 模型,不能外挂 VAD 或说话人模型。完整的
|
||||
`funasr-server`、Docker Compose、Kubernetes、vLLM、SGLang Omni、LocalAI 与
|
||||
FunClip 路径见 [MOSS 部署指南](../../docs/moss_transcribe_diarize_zh.md)。
|
||||
|
||||
## API 端点
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@ If the workflow engine runs in Docker, `localhost` usually means the workflow co
|
||||
|
||||
Before configuring a low-code tool, you can import the [Postman collection](POSTMAN.md) and run health, model-list, and transcription requests from a GUI. For schema-driven imports, use the [OpenAPI spec](OPENAPI.md). Set `FUNASR_BASE_URL`, choose a local audio file for the multipart `file` field, and keep `MODEL_ALIAS=sensevoice` for the first test.
|
||||
|
||||
For offline multi-speaker meetings, set `MODEL_ALIAS=moss-transcribe-diarize`
|
||||
and keep `response_format=verbose_json` so downstream nodes receive native
|
||||
anonymous speaker segments. Use the [MOSS deployment guide](../../docs/moss_transcribe_diarize.md)
|
||||
for the isolated GPU service and file-duration limits.
|
||||
|
||||
## Multipart HTTP request
|
||||
|
||||
Every workflow engine eventually needs to send this request shape:
|
||||
|
||||
@@ -25,6 +25,10 @@ curl -fsS "$FUNASR_BASE_URL/v1/models"
|
||||
|
||||
在配置低代码工具前,可以先导入 [Postman collection](POSTMAN_zh.md),从图形界面跑通 health、模型列表和转写请求;需要按 schema 导入时可使用 [OpenAPI spec](OPENAPI_zh.md)。设置 `FUNASR_BASE_URL`,在 multipart `file` 字段选择本地音频文件,第一次测试建议保持 `MODEL_ALIAS=sensevoice`。
|
||||
|
||||
处理离线多人会议时,将 `MODEL_ALIAS` 改为 `moss-transcribe-diarize`,并保留
|
||||
`response_format=verbose_json`,下游节点即可收到模型原生匿名说话人 segments。
|
||||
独立 GPU 服务与文件时长边界见 [MOSS 部署指南](../../docs/moss_transcribe_diarize_zh.md)。
|
||||
|
||||
## Multipart HTTP 请求
|
||||
|
||||
所有工作流引擎最终都需要发出下面这种请求:
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
services:
|
||||
funasr-moss-api:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: examples/openai_api/Dockerfile.moss
|
||||
image: funasr-moss-api:local
|
||||
ports:
|
||||
- "${FUNASR_HOST_PORT:-8000}:8000"
|
||||
environment:
|
||||
FUNASR_PORT: "8000"
|
||||
FUNASR_DEVICE: "${FUNASR_DEVICE:-cuda:0}"
|
||||
FUNASR_MODEL: moss-transcribe-diarize
|
||||
HF_HOME: /root/.cache/huggingface
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [gpu]
|
||||
volumes:
|
||||
- funasr-moss-cache:/root/.cache
|
||||
shm_size: "8gb"
|
||||
|
||||
volumes:
|
||||
funasr-moss-cache:
|
||||
@@ -57,6 +57,11 @@ For in-cluster clients, use `http://funasr-api.speech.svc.cluster.local:8000` as
|
||||
| Memory request | `8Gi` | Tune after observing startup and real audio workloads. |
|
||||
| Startup probe | 10 minutes | Increase if your registry, model hub, or storage backend is slow. |
|
||||
|
||||
The `moss-transcribe-diarize` alias uses a separate GPU image and resource profile. Build
|
||||
`examples/openai_api/Dockerfile.moss`, then apply `funasr-moss-api.yaml` after
|
||||
replacing `funasr-moss-api:local` with your registry's immutable image digest.
|
||||
The complete runtime matrix is in the [MOSS deployment guide](../../../docs/moss_transcribe_diarize.md).
|
||||
|
||||
## GPU notes
|
||||
|
||||
The example Dockerfile is CPU-first. For GPU clusters you need to adapt the image to CUDA-capable PyTorch/FunASR dependencies, then add your cluster's GPU scheduling fields, for example:
|
||||
|
||||
@@ -57,6 +57,11 @@ python smoke_test.py --base-url http://localhost:8000
|
||||
| 内存 request | `8Gi` | 根据启动过程和真实音频负载观测结果调整。 |
|
||||
| Startup probe | 10 分钟 | registry、模型下载或存储后端较慢时增大。 |
|
||||
|
||||
`moss-transcribe-diarize` 使用独立的 GPU 镜像与资源配置。先构建
|
||||
`examples/openai_api/Dockerfile.moss`,将 `funasr-moss-api.yaml` 中的
|
||||
`funasr-moss-api:local` 替换为内部镜像仓库的不可变 digest 后再应用。完整运行矩阵
|
||||
见 [MOSS 部署指南](../../../docs/moss_transcribe_diarize_zh.md)。
|
||||
|
||||
## GPU 说明
|
||||
|
||||
示例 Dockerfile 默认面向 CPU。GPU 集群需要先把镜像改成 CUDA-capable PyTorch/FunASR 依赖,再根据集群增加 GPU 调度字段,例如:
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: funasr-moss-cache
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 40Gi
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: funasr-moss-api
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: funasr-moss-api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: funasr-moss-api
|
||||
spec:
|
||||
containers:
|
||||
- name: api
|
||||
image: funasr-moss-api:local
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: FUNASR_MODEL
|
||||
value: moss-transcribe-diarize
|
||||
- name: FUNASR_DEVICE
|
||||
value: cuda:0
|
||||
- name: FUNASR_PORT
|
||||
value: "8000"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8000
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 60
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
cpu: "4"
|
||||
memory: 24Gi
|
||||
nvidia.com/gpu: "1"
|
||||
limits:
|
||||
memory: 48Gi
|
||||
nvidia.com/gpu: "1"
|
||||
volumeMounts:
|
||||
- name: cache
|
||||
mountPath: /root/.cache
|
||||
- name: dshm
|
||||
mountPath: /dev/shm
|
||||
volumes:
|
||||
- name: cache
|
||||
persistentVolumeClaim:
|
||||
claimName: funasr-moss-cache
|
||||
- name: dshm
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
sizeLimit: 8Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: funasr-moss-api
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: funasr-moss-api
|
||||
ports:
|
||||
- name: http
|
||||
port: 8000
|
||||
targetPort: http
|
||||
@@ -181,7 +181,8 @@
|
||||
"sensevoice",
|
||||
"paraformer",
|
||||
"paraformer-en",
|
||||
"fun-asr-nano"
|
||||
"fun-asr-nano",
|
||||
"moss-transcribe-diarize"
|
||||
],
|
||||
"default": "sensevoice",
|
||||
"description": "FunASR model alias."
|
||||
|
||||
@@ -6,6 +6,7 @@ Works with any agent framework that supports OpenAI audio API.
|
||||
|
||||
Usage:
|
||||
python server.py --model sensevoice --device cuda --port 8000
|
||||
python server.py --model moss-transcribe-diarize --device cuda:0 --port 8000
|
||||
|
||||
Then use with any OpenAI-compatible client:
|
||||
curl http://localhost:8000/v1/audio/transcriptions \
|
||||
@@ -54,6 +55,13 @@ MODEL_CONFIGS = {
|
||||
"vad_model": "fsmn-vad",
|
||||
"vad_kwargs": {"max_single_segment_time": 30000},
|
||||
},
|
||||
"moss-transcribe-diarize": {
|
||||
"model": "OpenMOSS-Team/MOSS-Transcribe-Diarize",
|
||||
"model_revision": "e8681d68e7042738ffca8ac8212bc8fcb1131ab8",
|
||||
"hub": "hf",
|
||||
"backend": "hf",
|
||||
"trust_remote_code": True,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +107,7 @@ async def transcribe(
|
||||
|
||||
Accepts the same parameters as OpenAI's /v1/audio/transcriptions:
|
||||
- file: Audio file (wav, mp3, flac, m4a, ogg, webm)
|
||||
- model: Model to use (sensevoice, paraformer, fun-asr-nano)
|
||||
- model: Model to use (sensevoice, paraformer, fun-asr-nano, moss-transcribe-diarize)
|
||||
- language: Optional language hint
|
||||
- response_format: json or verbose_json
|
||||
"""
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""FunASR Server — unified vLLM-based inference service.
|
||||
|
||||
Provides OpenAI-compatible API (/v1/audio/transcriptions) and REST API (/asr).
|
||||
Uses vLLM for Fun-ASR-Nano (GPU) or falls back to AutoModel for non-LLM models (SenseVoice/Paraformer).
|
||||
Uses vLLM for Fun-ASR-Nano (GPU) or AutoModel for supported models, including
|
||||
MOSS-Transcribe-Diarize's joint transcription and anonymous speaker labels.
|
||||
"""
|
||||
|
||||
import io
|
||||
@@ -32,6 +33,8 @@ PACKAGE_VERSION = (Path(__file__).resolve().parents[1] / "version.txt").read_tex
|
||||
|
||||
|
||||
_LANGUAGE_TAG_RE = re.compile(r"<\|(zh|en|yue|ja|ko)\|>")
|
||||
MOSS_MODEL_REVISION = "e8681d68e7042738ffca8ac8212bc8fcb1131ab8"
|
||||
NATIVE_DIARIZATION_MODELS = {"moss-transcribe-diarize"}
|
||||
|
||||
|
||||
def extract_language_from_asr_text(text):
|
||||
@@ -257,6 +260,13 @@ def create_app(
|
||||
"vad_model": "fsmn-vad",
|
||||
"punc_model": "ct-punc",
|
||||
},
|
||||
"moss-transcribe-diarize": {
|
||||
"model": "OpenMOSS-Team/MOSS-Transcribe-Diarize",
|
||||
"model_revision": MOSS_MODEL_REVISION,
|
||||
"hub": "hf",
|
||||
"backend": "hf",
|
||||
"trust_remote_code": True,
|
||||
},
|
||||
}
|
||||
|
||||
def _load_spk_model():
|
||||
@@ -335,7 +345,7 @@ def create_app(
|
||||
if app.state.model_path:
|
||||
cfg["model"] = app.state.model_path
|
||||
cfg["hub"] = app.state.hub
|
||||
elif app.state.hub:
|
||||
elif app.state.hub and "hub" not in cfg:
|
||||
cfg["hub"] = app.state.hub
|
||||
cfg["device"] = device
|
||||
cfg["disable_update"] = True
|
||||
@@ -434,7 +444,7 @@ def create_app(
|
||||
segments.append(segment)
|
||||
if not segments and text:
|
||||
segments = build_openai_fallback_segments(text, duration)
|
||||
if use_spk and segments:
|
||||
if use_spk and model_name not in NATIVE_DIARIZATION_MODELS and segments:
|
||||
audio_data, sr = sf.read(audio_path)
|
||||
attach_speaker_labels(
|
||||
audio_data,
|
||||
|
||||
+10
-1
@@ -5,6 +5,7 @@ Usage:
|
||||
funasr-server # default: sensevoice on cuda:0, port 8000
|
||||
funasr-server --device cpu --port 9000
|
||||
funasr-server --model paraformer
|
||||
funasr-server --model moss-transcribe-diarize --device cuda:0
|
||||
funasr-server --model-path /path/to/local/model
|
||||
funasr-server --model-path username/paraformer --hub hf
|
||||
funasr-server --cors-origin http://localhost:3000
|
||||
@@ -31,6 +32,7 @@ Examples:
|
||||
funasr-server # Start with SenseVoice on GPU
|
||||
funasr-server --device cpu # Start on CPU
|
||||
funasr-server --model paraformer # Use Paraformer model
|
||||
funasr-server --model moss-transcribe-diarize --device cuda:0
|
||||
funasr-server --port 9000 # Custom port
|
||||
funasr-server --model-path /path/to/local/model # Use local model
|
||||
funasr-server --model-path username/model --hub hf # Use HuggingFace model
|
||||
@@ -45,7 +47,14 @@ Then use with OpenAI SDK:
|
||||
parser.add_argument("--host", default="0.0.0.0", help="Bind address (default: 0.0.0.0)")
|
||||
parser.add_argument("--port", type=int, default=8000, help="Port (default: 8000)")
|
||||
parser.add_argument("--device", default="cuda", help="Device: cuda, cpu, mps (default: cuda)")
|
||||
parser.add_argument("--model", default="auto", help="Pre-load model: auto (GPU=fun-asr-nano, CPU=sensevoice), sensevoice, paraformer, fun-asr-nano")
|
||||
parser.add_argument(
|
||||
"--model",
|
||||
default="auto",
|
||||
help=(
|
||||
"Pre-load model: auto (GPU=fun-asr-nano, CPU=sensevoice), "
|
||||
"sensevoice, paraformer, fun-asr-nano, moss-transcribe-diarize"
|
||||
),
|
||||
)
|
||||
parser.add_argument("--model-path", default=None, help="Local model path or model ID (overrides --model)")
|
||||
parser.add_argument("--hub", default="ms", help="Model hub: ms (ModelScope), hf (HuggingFace) (default: ms)")
|
||||
parser.add_argument(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -78,3 +79,82 @@ def test_all_deployment_matrices_link_moss_guide(matrix: Path) -> None:
|
||||
"FunASR 소유 model 또는 `AutoModel` backend가 아닙니다",
|
||||
):
|
||||
assert stale_claim not in moss_row
|
||||
|
||||
|
||||
def test_moss_guides_cover_funasr_service_and_offline_boundaries() -> None:
|
||||
for guide in GUIDES:
|
||||
text = guide.read_text(encoding="utf-8")
|
||||
assert "funasr-server --model moss-transcribe-diarize" in text
|
||||
assert "response_format=verbose_json" in text
|
||||
assert "examples/openai_api/docker-compose.moss.yml" in text
|
||||
assert "examples/openai_api/kubernetes/funasr-moss-api.yaml" in text
|
||||
assert "WebSocket" in text
|
||||
assert "FunClip" in text
|
||||
|
||||
|
||||
def test_moss_service_recipes_are_pinned_and_gpu_scoped() -> None:
|
||||
dockerfile = ROOT / "examples" / "openai_api" / "Dockerfile.moss"
|
||||
compose = ROOT / "examples" / "openai_api" / "docker-compose.moss.yml"
|
||||
kubernetes = (
|
||||
ROOT / "examples" / "openai_api" / "kubernetes" / "funasr-moss-api.yaml"
|
||||
)
|
||||
|
||||
assert dockerfile.is_file()
|
||||
assert compose.is_file()
|
||||
assert kubernetes.is_file()
|
||||
docker_text = dockerfile.read_text(encoding="utf-8")
|
||||
compose_text = compose.read_text(encoding="utf-8")
|
||||
kubernetes_text = kubernetes.read_text(encoding="utf-8")
|
||||
for text in (docker_text, compose_text, kubernetes_text):
|
||||
assert "moss-transcribe-diarize" in text
|
||||
assert "transformers>=5.6,<6" in docker_text
|
||||
assert "capabilities: [gpu]" in compose_text
|
||||
assert "nvidia.com/gpu" in kubernetes_text
|
||||
|
||||
|
||||
def test_github_pages_indexes_moss_deployment_guide() -> None:
|
||||
index = (ROOT / "docs" / "index.rst").read_text(encoding="utf-8")
|
||||
assert "./moss_transcribe_diarize.md" in index
|
||||
assert "./moss_transcribe_diarize_zh.md" in index
|
||||
|
||||
|
||||
def test_readmes_link_the_runnable_moss_service() -> None:
|
||||
readmes = [
|
||||
ROOT / "README.md",
|
||||
ROOT / "README_zh.md",
|
||||
ROOT / "README_ja.md",
|
||||
ROOT / "README_ko.md",
|
||||
ROOT / "examples" / "openai_api" / "README.md",
|
||||
ROOT / "examples" / "openai_api" / "README_zh.md",
|
||||
ROOT / "examples" / "openai_api" / "README_ja.md",
|
||||
ROOT / "examples" / "openai_api" / "README_ko.md",
|
||||
]
|
||||
for readme in readmes:
|
||||
text = readme.read_text(encoding="utf-8")
|
||||
assert "moss-transcribe-diarize" in text
|
||||
assert "moss_transcribe_diarize" in text
|
||||
|
||||
|
||||
def test_openai_consumer_docs_expose_moss_alias_and_boundaries() -> None:
|
||||
paths = [
|
||||
"CLIENTS.md",
|
||||
"GRADIO.md",
|
||||
"GRADIO_zh.md",
|
||||
"OPENAPI.md",
|
||||
"OPENAPI_zh.md",
|
||||
"WORKFLOWS.md",
|
||||
"WORKFLOWS_zh.md",
|
||||
"JAVASCRIPT.md",
|
||||
"JAVASCRIPT_zh.md",
|
||||
"kubernetes/README.md",
|
||||
"kubernetes/README_zh.md",
|
||||
]
|
||||
root = ROOT / "examples" / "openai_api"
|
||||
for relative in paths:
|
||||
text = (root / relative).read_text(encoding="utf-8")
|
||||
assert "moss-transcribe-diarize" in text
|
||||
assert "moss_transcribe_diarize" in text
|
||||
|
||||
spec = json.loads((root / "openapi.json").read_text(encoding="utf-8"))
|
||||
model = spec["components"]["schemas"]["TranscriptionRequest"]["properties"]["model"]
|
||||
assert "moss-transcribe-diarize" in model["enum"]
|
||||
|
||||
@@ -442,6 +442,53 @@ def test_spk_request_lazily_loads_and_reuses_speaker_model(monkeypatch):
|
||||
]
|
||||
|
||||
|
||||
def test_moss_service_uses_pinned_joint_transcription_config(monkeypatch):
|
||||
module = load_server_app(monkeypatch)
|
||||
DummyAutoModel = install_dummy_funasr(monkeypatch)
|
||||
|
||||
module.create_app(device="cuda:0", preload_model="moss-transcribe-diarize")
|
||||
|
||||
config = DummyAutoModel.instances[-1]
|
||||
assert config["model"] == "OpenMOSS-Team/MOSS-Transcribe-Diarize"
|
||||
assert config["model_revision"] == "e8681d68e7042738ffca8ac8212bc8fcb1131ab8"
|
||||
assert config["hub"] == "hf"
|
||||
assert config["backend"] == "hf"
|
||||
assert config["trust_remote_code"] is True
|
||||
assert "vad_model" not in config
|
||||
assert "spk_model" not in config
|
||||
|
||||
|
||||
def test_moss_verbose_json_preserves_native_speaker_segments(monkeypatch):
|
||||
module = load_server_app(monkeypatch)
|
||||
DummyAutoModel = install_dummy_funasr(
|
||||
monkeypatch,
|
||||
generated_result={
|
||||
"text": "hello again",
|
||||
"sentence_info": [
|
||||
{"start": 100, "end": 900, "spk": "S01", "text": "hello"},
|
||||
{"start": 950, "end": 1700, "spk": "S02", "sentence": "again"},
|
||||
],
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(module.sf, "info", lambda path: types.SimpleNamespace(duration=1.7))
|
||||
app = module.create_app(device="cuda:0", preload_model="moss-transcribe-diarize")
|
||||
transcribe = app.routes[("POST", "/v1/audio/transcriptions")]
|
||||
|
||||
response = asyncio.run(
|
||||
transcribe(
|
||||
file=DummyUpload(),
|
||||
model="moss-transcribe-diarize",
|
||||
language=None,
|
||||
response_format="verbose_json",
|
||||
spk=True,
|
||||
)
|
||||
)
|
||||
|
||||
assert [segment["speaker"] for segment in response["segments"]] == ["S01", "S02"]
|
||||
assert [segment["text"] for segment in response["segments"]] == ["hello", "again"]
|
||||
assert not any(config.get("model") == "cam++" for config in DummyAutoModel.instances)
|
||||
|
||||
|
||||
def test_server_versions_follow_package_version(monkeypatch):
|
||||
expected = (REPO_ROOT / "funasr" / "version.txt").read_text().strip()
|
||||
module = load_server_app(monkeypatch)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"verified": "2026-08-30",
|
||||
"verified": "2026-09-01",
|
||||
"deployments": [
|
||||
{
|
||||
"id": "moss-transcribe-diarize",
|
||||
@@ -13,7 +13,7 @@
|
||||
"models": ["OpenMOSS-Team/MOSS-Transcribe-Diarize (third-party Apache-2.0 model)"],
|
||||
"operating_systems": ["Linux"],
|
||||
"interfaces": ["OpenAI-compatible HTTP", "vLLM", "SGLang Omni", "Transformers", "LocalAI / moss-transcribe.cpp"],
|
||||
"tested": {"funasr": "AutoModel vLLM + SGLang adapters; third-party model@e8681d68", "runtime": "vLLM 0.27.1 / Torch 2.13.0+cu129 / H100 80GB", "verified": "2026-08-30"},
|
||||
"tested": {"funasr": "AutoModel HF + vLLM + SGLang adapters and OpenAI HTTP service; third-party model@e8681d68", "runtime": "Transformers 5.16.0.dev0 + Torch 2.11.0+cu130 and vLLM 0.27.1 + Torch 2.13.0+cu129 / H100 80GB", "verified": "2026-09-01"},
|
||||
"commands": {
|
||||
"install": [
|
||||
"uv venv --python 3.12 .venv-moss && 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 pip install --python .venv-moss/bin/python --torch-backend=auto \"vllm[audio] @ file://$PWD/vllm-0.27.1+cu129-cp38-abi3-manylinux_2_28_x86_64.whl\"",
|
||||
@@ -33,6 +33,31 @@
|
||||
]
|
||||
},
|
||||
"runtime_paths": [
|
||||
{
|
||||
"id": "funasr-server",
|
||||
"tested": "Transformers 5.16.0.dev0 / Torch 2.11.0+cu130 / H100 80GB; real HTTP response verified 2026-09-01",
|
||||
"translations": {
|
||||
"zh": {"name": "FunASR OpenAI 兼容服务", "summary": "直接使用内置 funasr-server 加载固定 revision 的 HF 后端,返回带匿名说话人标签的 verbose_json;可使用专用 Docker Compose 与 Kubernetes GPU 配方。"},
|
||||
"en": {"name": "FunASR OpenAI-compatible service", "summary": "Load the pinned HF backend directly with funasr-server and return verbose_json with anonymous speaker labels; dedicated Docker Compose and Kubernetes GPU recipes are included."}
|
||||
},
|
||||
"commands": {
|
||||
"install": [
|
||||
"python -m pip install 'transformers>=5.6,<6' fastapi uvicorn python-multipart"
|
||||
],
|
||||
"launch": [
|
||||
"funasr-server --model moss-transcribe-diarize --device cuda:0 --port 8000",
|
||||
"docker compose -f examples/openai_api/docker-compose.moss.yml up --build"
|
||||
],
|
||||
"health": [
|
||||
"curl -fsS http://127.0.0.1:8000/health",
|
||||
"curl -fsS http://127.0.0.1:8000/v1/models"
|
||||
],
|
||||
"smoke": [
|
||||
"curl -fsS http://127.0.0.1:8000/v1/audio/transcriptions -F file=@runtime/llama.cpp/tests/sample.wav -F model=moss-transcribe-diarize -F response_format=verbose_json | tee /tmp/funasr-moss-transcription.json",
|
||||
"python - <<'PY'\nimport json\n\nwith open('/tmp/funasr-moss-transcription.json', encoding='utf-8') as stream:\n payload = json.load(stream)\nsegments = payload.get('segments', [])\nassert payload.get('text', '').strip() and segments, payload\nassert all(item.get('speaker') and item.get('start') <= item.get('end') for item in segments), payload\nprint(payload['text'], sorted({item['speaker'] for item in segments}))\nPY"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "vllm",
|
||||
"tested": "vLLM 0.27.1 / Torch 2.13.0+cu129 / H100 80GB; FunASR adapter verified",
|
||||
|
||||
@@ -128,7 +128,7 @@ def test_detail_commands_come_from_registry(built_site):
|
||||
assert command in rendered
|
||||
|
||||
|
||||
def test_moss_detail_renders_separate_vllm_and_sglang_runtime_paths(built_site):
|
||||
def test_moss_detail_renders_all_service_runtime_paths(built_site):
|
||||
for relative in (
|
||||
'deploy/moss-transcribe-diarize.html',
|
||||
'en/deploy/moss-transcribe-diarize.html',
|
||||
@@ -136,8 +136,14 @@ def test_moss_detail_renders_separate_vllm_and_sglang_runtime_paths(built_site):
|
||||
soup = read_soup(built_site / relative)
|
||||
paths = soup.select('[data-runtime-path]')
|
||||
|
||||
assert [path['data-runtime-path'] for path in paths] == ['vllm', 'sglang-omni']
|
||||
assert [path['data-runtime-path'] for path in paths] == [
|
||||
'funasr-server',
|
||||
'vllm',
|
||||
'sglang-omni',
|
||||
]
|
||||
rendered = '\n'.join(path.get_text('\n') for path in paths)
|
||||
assert 'funasr-server --model moss-transcribe-diarize' in rendered
|
||||
assert 'docker-compose.moss.yml' in rendered
|
||||
assert 'vllm serve OpenMOSS-Team/MOSS-Transcribe-Diarize' in rendered
|
||||
assert 'sgl-omni serve' in rendered
|
||||
assert 'response_format=diarized_json' in rendered
|
||||
|
||||
@@ -107,15 +107,24 @@ def test_moss_transcribe_diarize_contract_tracks_third_party_upstream(valid_regi
|
||||
'OpenMOSS-Team/MOSS-Transcribe-Diarize (third-party Apache-2.0 model)'
|
||||
]
|
||||
assert entry['tested'] == {
|
||||
'funasr': 'AutoModel vLLM + SGLang adapters; third-party model@e8681d68',
|
||||
'runtime': 'vLLM 0.27.1 / Torch 2.13.0+cu129 / H100 80GB',
|
||||
'verified': '2026-08-30',
|
||||
'funasr': 'AutoModel HF + vLLM + SGLang adapters and OpenAI HTTP service; third-party model@e8681d68',
|
||||
'runtime': 'Transformers 5.16.0.dev0 + Torch 2.11.0+cu130 and vLLM 0.27.1 + Torch 2.13.0+cu129 / H100 80GB',
|
||||
'verified': '2026-09-01',
|
||||
}
|
||||
assert 'LocalAI / moss-transcribe.cpp' in entry['interfaces']
|
||||
assert {'cpu', 'desktop-edge-gpu'} <= set(entry['hardware'])
|
||||
|
||||
runtime_paths = {path['id']: path for path in entry['runtime_paths']}
|
||||
assert set(runtime_paths) == {'vllm', 'sglang-omni'}
|
||||
assert set(runtime_paths) == {'funasr-server', 'vllm', 'sglang-omni'}
|
||||
service_commands = '\n'.join(
|
||||
command
|
||||
for group in ('install', 'launch', 'health', 'smoke')
|
||||
for command in runtime_paths['funasr-server']['commands'][group]
|
||||
)
|
||||
assert 'funasr-server --model moss-transcribe-diarize' in service_commands
|
||||
assert 'docker-compose.moss.yml' in service_commands
|
||||
assert 'response_format=verbose_json' in service_commands
|
||||
assert 'real HTTP response verified 2026-09-01' in runtime_paths['funasr-server']['tested']
|
||||
assert runtime_paths['vllm']['tested'] == (
|
||||
'vLLM 0.27.1 / Torch 2.13.0+cu129 / H100 80GB; FunASR adapter verified'
|
||||
)
|
||||
@@ -222,7 +231,7 @@ def test_audio_cpp_contract_tracks_mainline_nano_and_sensevoice(valid_registry):
|
||||
entry = next(item for item in valid_registry['deployments'] if item['id'] == 'audio-cpp')
|
||||
llama_cpp = next(item for item in valid_registry['deployments'] if item['id'] == 'llama-cpp')
|
||||
|
||||
assert valid_registry['verified'] == '2026-08-30'
|
||||
assert valid_registry['verified'] == '2026-09-01'
|
||||
assert entry['maturity'] == 'community-verified'
|
||||
assert entry['selector_rank'] > llama_cpp['selector_rank']
|
||||
assert entry['tested'] == {
|
||||
@@ -391,7 +400,7 @@ def test_sensevoice_native_server_contract_tracks_merged_runtime(valid_registry)
|
||||
llama_cpp = next(item for item in valid_registry['deployments'] if item['id'] == 'llama-cpp')
|
||||
audio_cpp = next(item for item in valid_registry['deployments'] if item['id'] == 'audio-cpp')
|
||||
|
||||
assert valid_registry['verified'] == '2026-08-30'
|
||||
assert valid_registry['verified'] == '2026-09-01'
|
||||
assert entry['maturity'] == 'production-verified'
|
||||
assert llama_cpp['selector_rank'] < entry['selector_rank'] < audio_cpp['selector_rank']
|
||||
assert entry['tested'] == {
|
||||
@@ -485,9 +494,9 @@ def test_runtime_paths_require_unique_ids_translations_and_commands(valid_regist
|
||||
item for item in data['deployments']
|
||||
if item['id'] == 'moss-transcribe-diarize'
|
||||
)
|
||||
entry['runtime_paths'][1]['id'] = 'vllm'
|
||||
del entry['runtime_paths'][0]['translations']['en']['summary']
|
||||
del entry['runtime_paths'][1]['commands']['health']
|
||||
entry['runtime_paths'][2]['id'] = 'vllm'
|
||||
del entry['runtime_paths'][1]['translations']['en']['summary']
|
||||
del entry['runtime_paths'][2]['commands']['health']
|
||||
|
||||
errors = validate_registry(data)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user