mirror of
https://github.com/modelscope/FunASR.git
synced 2026-10-12 02:19:18 +00:00
235 lines
15 KiB
Markdown
235 lines
15 KiB
Markdown
(English|[简体中文](README_zh.md)|[日本語](README_ja.md)|[한국어](README_ko.md))
|
|
|
|
# FunASR OpenAI-Compatible API Server
|
|
|
|
An OpenAI-style `/v1/audio/transcriptions` endpoint for private speech transcription. This example implements a speech API subset, not the entire OpenAI API or a compatibility guarantee for every SDK/framework feature.
|
|
|
|
This page starts the repository's [example server](server.py). The packaged `funasr-server` is a [different implementation](../../funasr/bin/_server_app.py); see [API boundaries](#api-contract) before reusing its settings. For in-process `AutoModel.generate()` rather than HTTP requests, use the [Python SDK guide](../../docs/python_api.md) ([中文](../../docs/python_api_zh.md)).
|
|
|
|
For the maintained packaged-service route, start with [Agent integration](../../docs/agent_integration.md). The example has **no built-in authentication or upload limit**; `api_key="not-needed"` does not authenticate it. Keep local testing on loopback. Before sharing, configure TLS, gateway authentication, upload/time/rate limits, audio/transcript retention, and private health/model/schema access using the [security guide](SECURITY.md).
|
|
|
|
## API Contract
|
|
|
|
- **Example `python server.py` in this directory:** startup and omitted multipart `model` both default to `sensevoice`. There is no `spk` form field; the example only preserves speaker labels already returned by the model.
|
|
- **Packaged `funasr-server`:** startup `--model auto` selects `fun-asr-nano` for a device string starting with `cuda`, otherwise `sensevoice`. Omitted multipart `model` independently defaults to `fun-asr-nano`. `spk=true` requests the separate speaker pipeline for non-native diarization models; default is `False`.
|
|
|
|
Specify `model` explicitly in requests: startup preloading and the request default are different settings. Query the deployed `/v1/models`; for example, `paraformer-en` is registered by this example but is not a built-in alias of the packaged server. Verify fields with the running `/openapi.json`, not just the checked-in [example schema](OPENAPI.md).
|
|
|
|
`response_format=verbose_json` selects a response shape; **it does not enable diarization or force timestamp generation**. This example copies `sentence_info` into `segments` if present, otherwise returns `segments=[]`. Speaker labels can be absent or null. MOSS supplies native anonymous labels; it does not need `spk=true` or external VAD/CAM++.
|
|
|
|
SDK output such as `timestamp`, or Nano's `timestamps` / `ctc_timestamps`, is not automatically converted into HTTP segments. This example accepts multipart `file`, `model`, `language`, and `response_format`; SDK options such as `use_itn`, hotwords, raw arrays, and `spk` are not its form fields. Its `language` is the submitted hint or `auto`, not detected language; the packaged service can use backend language detection.
|
|
|
|
In this example, `duration` is elapsed time around `generate()` in seconds, excluding initial model loading; it is **not audio duration**. The packaged server's verbose response uses audio duration in seconds (its fallback can use 0 when audio metadata is unavailable). Segment `start`/`end` use seconds in both services. The packaged fallback can synthesize coarse segments from text and audio duration; those are not word-level forced alignment. Its verbose schema includes `task` and per-segment `id`/`words`, while this example includes `model`; do not assume identical JSON fields. See [response examples and speaker requests](CLIENTS.md#api-contract).
|
|
|
|
## Quick Start
|
|
|
|
Use a fresh checkout and a POSIX shell with Python 3.11 installed:
|
|
|
|
```bash
|
|
git clone https://github.com/modelscope/FunASR.git FunASR-api
|
|
cd FunASR-api
|
|
git checkout --detach d91d961e37a005837b1523bcc6b09f087877be54
|
|
python3.11 -m venv .venv
|
|
source .venv/bin/activate
|
|
python -m pip install -e .
|
|
python -m pip install fastapi uvicorn python-multipart
|
|
python -m pip check
|
|
cd examples/openai_api
|
|
python server.py --host 127.0.0.1 --model sensevoice --device cpu --port 8000
|
|
```
|
|
|
|
This pins source only, not dependencies, model weights, audio decoders, or CUDA. A PyPI install alone does not provide repository examples. These are setup instructions, not evidence of a fresh installation or successful acoustic inference on your hardware.
|
|
|
|
Wait for model loading before checking `GET /health`; download and startup time depend on the checkpoint, cache, network, and hardware. Health alone does not verify transcription. The commands below use this directory unless stated otherwise. After preparing CUDA-capable dependencies, replace the CPU command with `python server.py --host 127.0.0.1 --model sensevoice --device cuda --port 8000`; do not start both on the same port.
|
|
|
|
Need copy-paste integration snippets for Python SDK, JavaScript/TypeScript, HTTP clients, agent tools, a browser demo, Postman, OpenAPI imports, Kubernetes deployment, or Dify/n8n-style workflows? See [Client recipes](CLIENTS.md), [JavaScript/TypeScript recipes](JAVASCRIPT.md), [Gradio browser demo](GRADIO.md), [workflow recipes](WORKFLOWS.md), the [Chinese workflow recipes](WORKFLOWS_zh.md), the [Postman collection](POSTMAN.md), the [OpenAPI spec](OPENAPI.md), the [security and gateway guide](SECURITY.md), and the [Kubernetes deployment template](kubernetes/README.md).
|
|
|
|
### End-to-end smoke test
|
|
|
|
In another terminal, enter the same checkout, activate `.venv`, and enter `examples/openai_api`. The optional scripts check health and transcription:
|
|
|
|
```bash
|
|
bash smoke_test.sh
|
|
# Cross-platform alternative without curl/bash:
|
|
python smoke_test.py
|
|
```
|
|
|
|
Equivalent manual commands using public Chinese sample audio, not a Japanese/Korean validation set:
|
|
|
|
```bash
|
|
curl -L https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/BAC009S0764W0121.wav -o sample.wav
|
|
curl http://localhost:8000/health
|
|
curl http://localhost:8000/v1/models
|
|
curl http://localhost:8000/openapi.json
|
|
curl http://localhost:8000/v1/audio/transcriptions \
|
|
-F file=@sample.wav \
|
|
-F model=sensevoice \
|
|
-F response_format=verbose_json
|
|
```
|
|
|
|
## Browser demo with Gradio
|
|
|
|
For local file upload or recorded microphone audio, follow the maintained [Gradio browser demo](GRADIO.md). It uses a separate Python 3.12 environment, `.venv-gradio`, rather than this API server's environment. The guide covers the `funasr`, `vllm`, and `sglang-omni` profiles, explicit model selection, Docker/Kubernetes connectivity, microphone permissions, and privacy limits. The UI is a separate HTTP client, not an authentication gateway or a realtime transcription service.
|
|
|
|
## Usage with OpenAI SDK (Python)
|
|
|
|
In the same activated environment, install the separate HTTP client with `python -m pip install openai`. This is not the FunASR Python SDK. Replace `meeting.wav` with a real local audio file supported by your prepared decoders.
|
|
|
|
```python
|
|
from openai import OpenAI
|
|
|
|
client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")
|
|
|
|
# Basic transcription
|
|
with open("meeting.wav", "rb") as audio:
|
|
result = client.audio.transcriptions.create(model="sensevoice", file=audio)
|
|
print(result.text)
|
|
|
|
# Inspect the verbose response; segments may be empty
|
|
with open("meeting.wav", "rb") as audio:
|
|
result = client.audio.transcriptions.create(
|
|
model="sensevoice", file=audio, response_format="verbose_json",
|
|
)
|
|
# verbose_json does not enable diarization; see API Contract above
|
|
print(getattr(result, "segments", []))
|
|
```
|
|
|
|
## Usage with curl
|
|
|
|
```bash
|
|
curl http://localhost:8000/v1/audio/transcriptions \
|
|
-F file=@audio.wav \
|
|
-F model=sensevoice
|
|
|
|
# With verbose output
|
|
curl http://localhost:8000/v1/audio/transcriptions \
|
|
-F file=@audio.wav \
|
|
-F model=sensevoice \
|
|
-F response_format=verbose_json
|
|
```
|
|
|
|
## Available Models
|
|
|
|
These are aliases in this example's `MODEL_CONFIGS`, not a universal SDK or server model list. The endpoint removes rich `<|...|>` tags from returned text; it does not expose dedicated emotion/event fields.
|
|
|
|
- `sensevoice`: SenseVoiceSmall + FSMN-VAD. Does not enable sentence timestamps or external speaker clustering by default.
|
|
- `paraformer`: `paraformer-zh` + FSMN-VAD + CT punctuation. Punctuation is configured; `verbose_json` alone does not request sentence records.
|
|
- `paraformer-en`: `paraformer-en` + FSMN-VAD. Example-only alias relative to the packaged server; no punctuation component configured here.
|
|
- `fun-asr-nano`: Fun-ASR-Nano via `AutoModel`, HF hub + FSMN-VAD. Not a vLLM route in this example. CTC timestamp availability depends on complete checkpoint weights.
|
|
- `moss-transcribe-diarize`: Third-party OpenMOSS native transcription/diarization adapter. Requires its separate dependency environment; preserves model-provided timestamps and anonymous labels.
|
|
|
|
For checkpoint-specific language and license information, use [model selection](../../docs/model_selection.md) and the model's own license. FunASR software's MIT license is not a license for every model weight. Benchmark the selected route on your own workload; these aliases do not define universal speed or capacity.
|
|
|
|
Fun-ASR-MLT-Nano is a separate multilingual checkpoint, not a built-in alias in either service; base Nano does not establish Korean support. For a custom checkpoint, the packaged route uses `--model-path` and `--hub` with request `model="custom"`; these are not example-server options. Follow the model-selection and Agent guides for checkpoint-specific setup.
|
|
|
|
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.
|
|
Its speaker labels are anonymous within one recording, not real-world identities or cross-recording speaker recognition. An alias appearing in `/v1/models` does not prove its dependencies or weights are ready.
|
|
|
|
## API Endpoints
|
|
|
|
| Endpoint | Method | Description |
|
|
|----------|--------|-------------|
|
|
| `/v1/audio/transcriptions` | POST | Transcribe audio (OpenAI-compatible) |
|
|
| `/v1/models` | GET | List available models |
|
|
| `/health` | GET | Health check + loaded models |
|
|
| `/docs` | GET | Interactive API documentation (Swagger) |
|
|
|
|
Prefer no-code API checks? Use the [Gradio browser demo](GRADIO.md) for local upload or microphone testing, or import the [Postman collection](POSTMAN.md) and run health, model-list, and transcription requests from Postman. For API gateways, developer portals, or client generation, use the [OpenAPI spec](OPENAPI.md).
|
|
|
|
## Agent Framework Integration
|
|
|
|
The multipart HTTP/tool-function pattern can be used to integrate **LangChain**, **LlamaIndex**, **AutoGen**, **CrewAI**, **Semantic Kernel**, **Dify**, and **n8n**; validate the chosen integration against this service's supported fields. These recipes do not establish compatibility with every framework version or realtime API. See [Client recipes](CLIENTS.md) and [JavaScript/TypeScript recipes](JAVASCRIPT.md) for SDK and agent-tool patterns, plus [workflow recipes](WORKFLOWS.md) for low-code HTTP nodes and webhook workers ([中文](WORKFLOWS_zh.md)).
|
|
|
|
Both services map the workflow request alias `whisper-1` to the startup-selected model; this does not run OpenAI Whisper. A workflow container's `localhost` refers to that container. Use an intentionally authorized reachable gateway/service address, not unrestricted public exposure.
|
|
|
|
### LangChain Example
|
|
```python
|
|
from openai import OpenAI
|
|
|
|
client = OpenAI(base_url="http://localhost:8000/v1", api_key="x")
|
|
|
|
def transcribe_for_agent(audio_path: str) -> str:
|
|
"""Tool function for LangChain agent."""
|
|
with open(audio_path, "rb") as audio:
|
|
result = client.audio.transcriptions.create(model="sensevoice", file=audio)
|
|
return result.text
|
|
```
|
|
|
|
## Docker Deployment
|
|
|
|
From the repository root, build the example image with the following commands. The default image starts the example `server.py` in CPU mode, not the packaged `funasr-server`.
|
|
|
|
This is a local-development publication setting, not authentication. The container still listens on `0.0.0.0`; only the host-published port binds to `127.0.0.1`. Do not change the container listener to loopback. The current Dockerfile installs unpinned PyPI FunASR/dependencies while copying this example, so it is not the source-pinned Python environment above or a reproducible acoustic environment.
|
|
|
|
```bash
|
|
cd examples/openai_api
|
|
cp .env.example .env
|
|
|
|
FUNASR_HOST_PORT=127.0.0.1:8000 docker compose up --build
|
|
```
|
|
|
|
Equivalent one-off `docker run` command:
|
|
|
|
```bash
|
|
docker build -t funasr-api .
|
|
|
|
docker run --rm -p 127.0.0.1:8000:8000 \
|
|
-e FUNASR_DEVICE=cpu \
|
|
-e FUNASR_MODEL=sensevoice \
|
|
funasr-api
|
|
```
|
|
|
|
For GPU hosts, use NVIDIA Container Toolkit and a CUDA-capable PyTorch/FunASR image. After adapting the image dependencies for CUDA, run the same server with `FUNASR_DEVICE=cuda`:
|
|
|
|
```bash
|
|
docker run --rm --gpus all -p 127.0.0.1:8000:8000 \
|
|
-e FUNASR_DEVICE=cuda \
|
|
-e FUNASR_MODEL=sensevoice \
|
|
funasr-api
|
|
```
|
|
|
|
Verify the container from another terminal:
|
|
|
|
```bash
|
|
BASE_URL=http://localhost:8000 bash smoke_test.sh
|
|
python smoke_test.py --base-url http://localhost:8000
|
|
```
|
|
|
|
The optional [validate_docker.sh](validate_docker.sh) combines build/run/smoke steps, but **its default port publication uses all host interfaces** and does not inherit the loopback settings above. Review its networking before running it; use the explicit loopback build/run/smoke recipe above for local testing on shared networks. Its GPU mode additionally requires NVIDIA Container Toolkit and a CUDA-capable image. These instructions are not evidence of a Docker or acoustic inference test.
|
|
|
|
## Kubernetes Deployment
|
|
|
|
Before sharing the service across a team or exposing it through a gateway, review the [security and gateway guide](SECURITY.md) for TLS, authentication, upload limits, rate limits, and logging.
|
|
|
|
For an internal cluster service with persistent model cache, health probes, and a private `ClusterIP`, start from the [Kubernetes deployment template](kubernetes/README.md). Build and push the example image, apply the manifests, then verify through `kubectl port-forward` with `python smoke_test.py --base-url http://localhost:8000`.
|
|
|
|
Keep the default CPU mode until you have built a CUDA-capable image and configured GPU scheduling for your cluster.
|
|
|
|
## Configuration
|
|
|
|
The following defaults belong to this example's `server.py`, not `funasr-server`; compare [API boundaries](#api-contract).
|
|
|
|
| Arg | Default | Description |
|
|
|-----|---------|-------------|
|
|
| `--host` | 0.0.0.0 | Bind address |
|
|
| `--port` | 8000 | Port |
|
|
| `--device` | cuda | Device (cuda/cpu/mps) |
|
|
| `--model` | sensevoice | Pre-load model at startup |
|
|
|
|
Docker environment variables:
|
|
|
|
| Env | Default | Description |
|
|
|-----|---------|-------------|
|
|
| `FUNASR_PORT` | 8000 | Container port passed to `server.py` |
|
|
| `FUNASR_DEVICE` | cpu | Container device mode; set to `cuda` only when the image has CUDA-capable dependencies |
|
|
| `FUNASR_MODEL` | sensevoice | Model alias loaded at container startup |
|
|
|
|
## Troubleshooting
|
|
|
|
- If CUDA is unavailable, use `--device cpu` for a slower but simple smoke test.
|
|
- If port 8000 is occupied, start with `--port 9000` and run `BASE_URL=http://localhost:9000 bash smoke_test.sh` or `python smoke_test.py --base-url http://localhost:9000`.
|
|
- If model download is slow, retry with a stable network or pre-download the model from ModelScope/Hugging Face.
|