+11








f50c5a413f
Signed-off-by: JuanPZuluaga <juanz9312@gmal.com> Signed-off-by: yiliu30 <yi4.liu@intel.com> Signed-off-by: gcanlin <canlinguosdu@gmail.com> Signed-off-by: Binh Tang <tangbinhna@gmail.com> Signed-off-by: Binh Tang <binht@netflix.com> Signed-off-by: Didan Deng <33117903+wtomin@users.noreply.github.com> Signed-off-by: Rein Yang <ruiruyang2@gmail.com> Signed-off-by: CHEN <116010019@link.cuhk.edu.cn> Signed-off-by: vraiti <vraiti@redhat.com> Signed-off-by: Songrui625 <songrui625@gmail.com> Signed-off-by: Lidang Jiang <lidangjiang@gmail.com> Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com> Signed-off-by: Alex Brooks <albrooks@redhat.com> Co-authored-by: JuanPZuluaga <juanz9312@gmal.com> Co-authored-by: Yi Liu <yi4.liu@intel.com> Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com> Co-authored-by: Canlin Guo <canlinguosdu@gmail.com> Co-authored-by: Binh Tang <tangbinhna@gmail.com> Co-authored-by: Binh Tang <binht@netflix.com> Co-authored-by: Didan Deng <33117903+wtomin@users.noreply.github.com> Co-authored-by: rein yang <73573651+R2-Y@users.noreply.github.com> Co-authored-by: zhumingjue138 <zhumingjue@huawei.com> Co-authored-by: ChenWenjing <54166744+Shirley125@users.noreply.github.com> Co-authored-by: vraiti <vraiti@redhat.com> Co-authored-by: 汪志鹏 <wangzhipeng628@gmail.com> Co-authored-by: Sy03 <1370724210@qq.com> Co-authored-by: chickeyton <ngton2014@gmail.com> Co-authored-by: Jerry Song <46962917+Songrui625@users.noreply.github.com> Co-authored-by: Lidang Jiang <119769478+Lidang-Jiang@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Alex Brooks <albrooks@redhat.com> Co-authored-by: linyueqian <linyueqian@outlook.com> Co-authored-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com>
627 lines
18 KiB
Markdown
627 lines
18 KiB
Markdown
# Speech API
|
||
|
||
vLLM-Omni provides an OpenAI-compatible API for text-to-speech (TTS) generation. Supported TTS models include:
|
||
|
||
- **Qwen3-TTS** (`Qwen/Qwen3-TTS-12Hz-*`) -- Qwen3-based TTS with CustomVoice, VoiceDesign, and Base (voice cloning) task types. Output: 24 kHz.
|
||
- **Fish Speech S2 Pro** (`fishaudio/s2-pro`) -- Dual-AR TTS with DAC codec. Supports text-to-speech and voice cloning via reference audio. Output: 44.1 kHz.
|
||
- **Voxtral TTS** (`mistralai/Voxtral-4B-TTS-2603`) -- AR + FlowMatching TTS with preset voices. Output: 24 kHz.
|
||
|
||
Each server instance runs a single model (specified at startup via `vllm serve <model> --omni`).
|
||
|
||
## Quick Start
|
||
|
||
### Start the Server
|
||
|
||
```bash
|
||
# Qwen3-TTS: CustomVoice model (predefined speakers)
|
||
vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \
|
||
--stage-configs-path vllm_omni/model_executor/stage_configs/qwen3_tts.yaml \
|
||
--omni \
|
||
--port 8091 \
|
||
--trust-remote-code \
|
||
--enforce-eager
|
||
|
||
# Fish Speech S2 Pro
|
||
vllm-omni serve fishaudio/s2-pro \
|
||
--stage-configs-path vllm_omni/model_executor/stage_configs/fish_speech_s2_pro.yaml \
|
||
--omni \
|
||
--port 8091 \
|
||
--trust-remote-code \
|
||
--enforce-eager \
|
||
--gpu-memory-utilization 0.9
|
||
|
||
# Voxtral TTS
|
||
vllm serve mistralai/Voxtral-4B-TTS-2603 \
|
||
--stage-configs-path vllm_omni/model_executor/stage_configs/voxtral_tts.yaml \
|
||
--omni \
|
||
--port 8091 \
|
||
--trust-remote-code \
|
||
--enforce-eager
|
||
```
|
||
|
||
### Generate Speech
|
||
|
||
**Using curl:**
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/speech \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"input": "Hello, how are you?",
|
||
"voice": "vivian",
|
||
"language": "English"
|
||
}' --output output.wav
|
||
```
|
||
|
||
**Using Python:**
|
||
|
||
```python
|
||
import httpx
|
||
|
||
response = httpx.post(
|
||
"http://localhost:8091/v1/audio/speech",
|
||
json={
|
||
"input": "Hello, how are you?",
|
||
"voice": "vivian",
|
||
"language": "English",
|
||
},
|
||
timeout=300.0,
|
||
)
|
||
|
||
with open("output.wav", "wb") as f:
|
||
f.write(response.content)
|
||
```
|
||
|
||
**Using OpenAI SDK:**
|
||
|
||
```python
|
||
from openai import OpenAI
|
||
|
||
client = OpenAI(base_url="http://localhost:8091/v1", api_key="none")
|
||
|
||
response = client.audio.speech.create(
|
||
model="Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice",
|
||
voice="vivian",
|
||
input="Hello, how are you?",
|
||
)
|
||
|
||
response.stream_to_file("output.wav")
|
||
```
|
||
|
||
## API Reference
|
||
|
||
### Endpoint
|
||
|
||
```
|
||
POST /v1/audio/speech
|
||
Content-Type: application/json
|
||
```
|
||
|
||
### Request Parameters
|
||
|
||
#### OpenAI Standard Parameters
|
||
|
||
| Parameter | Type | Default | Description |
|
||
|-----------|------|---------|-------------|
|
||
| `input` | string | **required** | The text to synthesize into speech |
|
||
| `model` | string | server's model | Model to use (optional, should match server if specified) |
|
||
| `voice` | string | "vivian" | Speaker name (e.g., vivian, ryan, aiden) |
|
||
| `response_format` | string | "wav" | Audio format: wav, mp3, flac, pcm, aac, opus |
|
||
| `speed` | float | 1.0 | Playback speed (0.25-4.0) |
|
||
|
||
#### vLLM-Omni Extension Parameters
|
||
|
||
| Parameter | Type | Default | Description |
|
||
|-----------|------|---------|-------------|
|
||
| `task_type` | string | "CustomVoice" | TTS task type: CustomVoice, VoiceDesign, or Base |
|
||
| `language` | string | "Auto" | Language (see supported languages below) |
|
||
| `instructions` | string | "" | Voice style/emotion instructions |
|
||
| `max_new_tokens` | integer | 2048 | Maximum tokens to generate |
|
||
| `initial_codec_chunk_frames` | integer | null | Per-request initial chunk size override for TTFA tuning. When null, IC is computed dynamically based on server load. |
|
||
| `stream` | bool | false | Stream raw PCM chunks as they are decoded (requires `response_format="pcm"`) |
|
||
|
||
**Supported languages:** Auto, Chinese, English, Japanese, Korean, German, French, Russian, Portuguese, Spanish, Italian
|
||
|
||
#### Voice Clone Parameters (Base task)
|
||
|
||
| Parameter | Type | Default | Description |
|
||
|-----------|------|---------|-------------|
|
||
| `ref_audio` | string | null | Reference audio (HTTP URL, base64 data URL, or `file://` URI with `--allowed-local-media-path`) |
|
||
| `ref_text` | string | null | Transcript of reference audio |
|
||
| `x_vector_only_mode` | bool | null | Use speaker embedding only (no ICL) |
|
||
|
||
### Response Format
|
||
|
||
Returns binary audio data with appropriate `Content-Type` header (e.g., `audio/wav`).
|
||
|
||
### Voices Endpoint
|
||
|
||
```
|
||
GET /v1/audio/voices
|
||
```
|
||
|
||
Lists available voices for the loaded model.
|
||
|
||
```json
|
||
{
|
||
"voices": ["aiden", "dylan", "eric", "ono_anna", "ryan", "serena", "sohee", "uncle_fu", "vivian", "custom_voice_1"],
|
||
"uploaded_voices": [
|
||
{
|
||
"name": "custom_voice_1",
|
||
"consent": "user_consent_id",
|
||
"created_at": 1738660000,
|
||
"file_size": 1024000,
|
||
"mime_type": "audio/wav",
|
||
"ref_text": "The exact transcript of the audio sample.",
|
||
"speaker_description": "warm narrator"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
`uploaded_voices` is always present (empty list when no custom voices have been uploaded). Fields `ref_text` and `speaker_description` are omitted per-entry when not provided at upload time.
|
||
|
||
```
|
||
POST /v1/audio/voices
|
||
Content-Type: multipart/form-data
|
||
```
|
||
|
||
Upload a new voice sample for voice cloning in Base task TTS requests.
|
||
|
||
**Form Parameters:**
|
||
|
||
| Parameter | Type | Required | Description |
|
||
|-----------|------|----------|-------------|
|
||
| `audio_sample` | file | Yes | Audio file (max 10MB, supported formats: wav, mp3, flac, ogg, aac, webm, mp4) |
|
||
| `consent` | string | Yes | Consent recording ID |
|
||
| `name` | string | Yes | Name for the new voice |
|
||
| `ref_text` | string | No | Transcript of the audio. When provided, enables in-context voice cloning (higher quality). Without it, only the speaker embedding is extracted. |
|
||
| `speaker_description` | string | No | Free-form description of the voice (e.g. "warm narrator", "energetic presenter"). Stored as metadata and returned in `GET /v1/audio/voices`. |
|
||
|
||
**Response Example:**
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"voice": {
|
||
"name": "custom_voice_1",
|
||
"consent": "user_consent_id",
|
||
"created_at": 1738660000,
|
||
"mime_type": "audio/wav",
|
||
"file_size": 1024000,
|
||
"ref_text": "The exact transcript of the audio sample.",
|
||
"speaker_description": "warm narrator"
|
||
}
|
||
}
|
||
```
|
||
|
||
Fields `ref_text` and `speaker_description` are omitted when not provided at upload time.
|
||
|
||
**Usage Example:**
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/voices \
|
||
-F "audio_sample=@/path/to/voice_sample.wav" \
|
||
-F "consent=user_consent_id" \
|
||
-F "name=custom_voice_1" \
|
||
-F "ref_text=The exact transcript of the audio sample." \
|
||
-F "speaker_description=warm narrator"
|
||
```
|
||
|
||
## Streaming Text Input (WebSocket)
|
||
|
||
The `/v1/audio/speech/stream` WebSocket endpoint accepts text incrementally and generates audio per sentence as boundaries are detected.
|
||
|
||
> Note: text input is always streamed incrementally. Audio output remains sentence-scoped:
|
||
> use `stream_audio=false` for one binary frame per sentence, or `stream_audio=true` for one or more PCM chunks per sentence.
|
||
|
||
### WebSocket Protocol
|
||
|
||
Client -> Server:
|
||
|
||
| Message | Description |
|
||
|---------|-------------|
|
||
| `{"type": "session.config", ...}` | Session configuration (sent once, first message) |
|
||
| `{"type": "input.text", "text": "..."}` | Text chunk |
|
||
| `{"type": "input.done"}` | End of input, flushes remaining buffer |
|
||
|
||
Server -> Client:
|
||
|
||
| Message | Description |
|
||
|---------|-------------|
|
||
| `{"type": "audio.start", "sentence_index": 0, "sentence_text": "...", "format": "pcm", "sample_rate": 24000}` | Audio generation starting for a sentence |
|
||
| Binary frame | Raw audio bytes (one or more PCM chunks when `stream_audio=true`) |
|
||
| `{"type": "audio.done", "sentence_index": 0, "total_bytes": 96000, "error": false}` | Audio complete for a sentence |
|
||
| `{"type": "session.done", "total_sentences": N}` | Session complete |
|
||
| `{"type": "error", "message": "..."}` | Non-fatal error |
|
||
|
||
### Session Config Parameters
|
||
|
||
All REST API parameters are supported, plus:
|
||
|
||
| Parameter | Type | Default | Description |
|
||
|-----------|------|---------|-------------|
|
||
| `stream_audio` | bool | false | Stream one or more PCM chunks per sentence over WebSocket |
|
||
| `split_granularity` | string | "sentence" | Text splitting granularity |
|
||
|
||
|
||
```bash
|
||
DELETE /v1/audio/voices/{name}
|
||
```
|
||
|
||
Delete an uploaded voice sample.
|
||
|
||
**Path Parameters:**
|
||
|
||
| Parameter | Type | Required | Description |
|
||
|-----------|------|----------|-------------|
|
||
| `name` | string | Yes | Name of the voice to delete |
|
||
|
||
**Response Example:**
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Voice 'custom_voice_1' deleted successfully"
|
||
}
|
||
```
|
||
|
||
**Error Response (404 Not Found):**
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"error": "Voice 'unknown_voice' not found"
|
||
}
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```bash
|
||
curl -X DELETE http://localhost:8091/v1/audio/voices/custom_voice_1
|
||
```
|
||
|
||
## Examples
|
||
|
||
### CustomVoice with Style Instruction
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/speech \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"input": "I am so excited!",
|
||
"voice": "vivian",
|
||
"instructions": "Speak with great enthusiasm"
|
||
}' --output excited.wav
|
||
```
|
||
|
||
### VoiceDesign (Natural Language Voice Description)
|
||
|
||
```bash
|
||
# Start server with VoiceDesign model first
|
||
vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign \
|
||
--stage-configs-path vllm_omni/model_executor/stage_configs/qwen3_tts.yaml \
|
||
--omni \
|
||
--port 8091 \
|
||
--trust-remote-code \
|
||
--enforce-eager
|
||
```
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/speech \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"input": "Hello world",
|
||
"task_type": "VoiceDesign",
|
||
"instructions": "A warm, friendly female voice with a gentle tone"
|
||
}' --output designed.wav
|
||
```
|
||
|
||
### Base (Voice Cloning)
|
||
|
||
```bash
|
||
# Start server with Base model first
|
||
vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-Base \
|
||
--stage-configs-path vllm_omni/model_executor/stage_configs/qwen3_tts.yaml \
|
||
--omni \
|
||
--port 8091 \
|
||
--trust-remote-code \
|
||
--enforce-eager
|
||
```
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/speech \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"input": "Hello, this is a cloned voice",
|
||
"task_type": "Base",
|
||
"ref_audio": "https://example.com/reference.wav",
|
||
"ref_text": "Original transcript of the reference audio"
|
||
}' --output cloned.wav
|
||
```
|
||
|
||
### Upload Voice
|
||
|
||
Upload voice (speaker embedding only):
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/voices \
|
||
-F "audio_sample=@/path/to/voice_sample.wav" \
|
||
-F "consent=user_consent_id" \
|
||
-F "name=custom_voice_1"
|
||
```
|
||
|
||
Upload voice with transcript (in-context cloning, higher quality):
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/voices \
|
||
-F "audio_sample=@/path/to/voice_sample.wav" \
|
||
-F "consent=user_consent_id" \
|
||
-F "name=custom_voice_2" \
|
||
-F "ref_text=The exact transcript of the audio sample."
|
||
```
|
||
|
||
### Use Uploaded Voice
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/speech \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"input": "Hello, this is a cloned voice",
|
||
"voice": "custom_voice_1"
|
||
}' --output cloned.wav
|
||
```
|
||
|
||
## Batch Speech Generation
|
||
|
||
The batch endpoint synthesizes multiple texts in a single request, returning all results as JSON with base64-encoded audio.
|
||
|
||
### Endpoint
|
||
|
||
```
|
||
POST /v1/audio/speech/batch
|
||
Content-Type: application/json
|
||
```
|
||
|
||
### Request Parameters
|
||
|
||
| Parameter | Type | Default | Description |
|
||
|-----------|------|---------|-------------|
|
||
| `items` | array | **required** | List of items to synthesize (1–32) |
|
||
| `model` | string | server's model | Model to use |
|
||
| `voice` | string | null | Default voice for all items |
|
||
| `response_format` | string | "wav" | Default audio format for all items |
|
||
| `speed` | float | 1.0 | Default playback speed (0.25–4.0) |
|
||
| `task_type` | string | null | Default TTS task type |
|
||
| `language` | string | null | Default language |
|
||
| `instructions` | string | null | Default voice style instructions |
|
||
| `ref_audio` | string | null | Default reference audio (Base task) |
|
||
| `ref_text` | string | null | Default reference transcript (Base task) |
|
||
| `max_new_tokens` | integer | null | Default max tokens |
|
||
|
||
Each item in the `items` array requires only `input` (the text). All other fields are optional and override the batch-level defaults when set:
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `input` | string | **required** — text to synthesize |
|
||
| `voice` | string | Override voice for this item |
|
||
| `response_format` | string | Override format for this item |
|
||
| `speed` | float | Override speed for this item |
|
||
| `task_type` | string | Override task type |
|
||
| `language` | string | Override language |
|
||
| `instructions` | string | Override instructions |
|
||
| `ref_audio` | string | Override reference audio |
|
||
| `ref_text` | string | Override reference transcript |
|
||
| `max_new_tokens` | integer | Override max tokens |
|
||
|
||
### Response Format
|
||
|
||
```json
|
||
{
|
||
"id": "speech-batch-abc123",
|
||
"results": [
|
||
{
|
||
"index": 0,
|
||
"status": "success",
|
||
"audio_data": "<base64-encoded audio>",
|
||
"media_type": "audio/wav"
|
||
},
|
||
{
|
||
"index": 1,
|
||
"status": "error",
|
||
"error": "Input text cannot be empty"
|
||
}
|
||
],
|
||
"total": 2,
|
||
"succeeded": 1,
|
||
"failed": 1
|
||
}
|
||
```
|
||
|
||
### Examples
|
||
|
||
**Basic batch with shared defaults:**
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/speech/batch \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"items": [
|
||
{"input": "Hello, how are you?"},
|
||
{"input": "Goodbye, see you later!"}
|
||
],
|
||
"voice": "vivian",
|
||
"language": "English"
|
||
}'
|
||
```
|
||
|
||
**Per-item overrides (different voices and formats):**
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/speech/batch \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"items": [
|
||
{"input": "Hello!", "voice": "vivian", "response_format": "mp3"},
|
||
{"input": "你好!", "voice": "ryan", "language": "Chinese"}
|
||
],
|
||
"response_format": "wav"
|
||
}'
|
||
```
|
||
|
||
**Voice cloning with shared reference audio (Base task):**
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8091/v1/audio/speech/batch \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"items": [
|
||
{"input": "First sentence in the cloned voice."},
|
||
{"input": "Second sentence in the cloned voice."}
|
||
],
|
||
"task_type": "Base",
|
||
"ref_audio": "https://example.com/reference.wav",
|
||
"ref_text": "Transcript of the reference audio"
|
||
}'
|
||
```
|
||
|
||
Setting `ref_audio` at the batch level applies it to all items, avoiding the need to repeat it per item.
|
||
|
||
**Decoding the response in Python:**
|
||
|
||
```python
|
||
import base64
|
||
import httpx
|
||
|
||
response = httpx.post(
|
||
"http://localhost:8091/v1/audio/speech/batch",
|
||
json={
|
||
"items": [
|
||
{"input": "First sentence."},
|
||
{"input": "Second sentence."},
|
||
],
|
||
"voice": "vivian",
|
||
},
|
||
timeout=300.0,
|
||
)
|
||
|
||
for result in response.json()["results"]:
|
||
if result["status"] == "success":
|
||
audio_bytes = base64.b64decode(result["audio_data"])
|
||
with open(f"output_{result['index']}.wav", "wb") as f:
|
||
f.write(audio_bytes)
|
||
```
|
||
|
||
### Configuration
|
||
|
||
| Parameter | Source | Default | Description |
|
||
|-----------|--------|---------|-------------|
|
||
| `tts_batch_max_items` | engine kwarg | 32 | Maximum number of items per batch request |
|
||
|
||
All items are fanned out to `generate()` concurrently. The engine's stage worker automatically batches them up to the configured `max_batch_size` and queues the rest — no client-side throttling needed.
|
||
|
||
For best throughput, use a batch-optimized stage config with `max_batch_size > 1`:
|
||
|
||
```bash
|
||
vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \
|
||
--stage-configs-path vllm_omni/model_executor/stage_configs/qwen3_tts_batch.yaml \
|
||
--omni --port 8091 --trust-remote-code --enforce-eager
|
||
```
|
||
|
||
The default `qwen3_tts.yaml` uses `max_batch_size: 1` (single request). The `qwen3_tts_batch.yaml` config sets `max_batch_size: 4` for ~4x throughput.
|
||
|
||
## Supported Models
|
||
|
||
### Qwen3-TTS
|
||
|
||
| Model | Task Type | Description |
|
||
|-------|-----------|-------------|
|
||
| `Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice` | CustomVoice | Predefined speaker voices with optional style control |
|
||
| `Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign` | VoiceDesign | Natural language voice style description |
|
||
| `Qwen/Qwen3-TTS-12Hz-1.7B-Base` | Base | Voice cloning from reference audio |
|
||
| `Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice` | CustomVoice | Smaller/faster variant |
|
||
| `Qwen/Qwen3-TTS-12Hz-0.6B-Base` | Base | Smaller/faster variant for voice cloning |
|
||
|
||
### Fish Speech S2 Pro
|
||
|
||
| Model | Description |
|
||
|-------|-------------|
|
||
| `fishaudio/s2-pro` | 4B dual-AR TTS with DAC codec (44.1 kHz). Supports text-to-speech and voice cloning. |
|
||
|
||
Fish Speech uses `ref_audio` and `ref_text` for voice cloning (no `task_type` needed). The `voice` field should be set to `"default"`. See the [Fish Speech online serving example](../user_guide/examples/online_serving/fish_speech.md) for details.
|
||
|
||
### Voxtral TTS
|
||
|
||
| Model | Description |
|
||
|-------|-------------|
|
||
| `mistralai/Voxtral-4B-TTS-2603` | 3B AR + FlowMatching TTS. Supports text-to-speech with preset voices. |
|
||
|
||
## Error Responses
|
||
|
||
### 400 Bad Request
|
||
|
||
Invalid parameters:
|
||
|
||
```json
|
||
{
|
||
"error": {
|
||
"message": "Input text cannot be empty",
|
||
"type": "BadRequestError",
|
||
"param": null,
|
||
"code": 400
|
||
}
|
||
}
|
||
```
|
||
|
||
### 404 Not Found
|
||
|
||
Model not found:
|
||
|
||
```json
|
||
{
|
||
"error": {
|
||
"message": "The model `xxx` does not exist.",
|
||
"type": "NotFoundError",
|
||
"param": "model",
|
||
"code": 404
|
||
}
|
||
}
|
||
```
|
||
|
||
## Troubleshooting
|
||
|
||
### "TTS model did not produce audio output"
|
||
|
||
Ensure you're using the correct model variant for your task type:
|
||
- CustomVoice task → CustomVoice model
|
||
- VoiceDesign task → VoiceDesign model
|
||
- Base task → Base model
|
||
|
||
### Server Not Running
|
||
|
||
```bash
|
||
# Check if server is responding
|
||
curl http://localhost:8091/v1/audio/voices
|
||
```
|
||
|
||
### Out of Memory
|
||
|
||
If you encounter OOM errors:
|
||
1. Use smaller model variant: `Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice`
|
||
2. Reduce `--gpu-memory-utilization`
|
||
|
||
### Unsupported Speaker
|
||
|
||
Use `/v1/audio/voices` to list available voices for the loaded model.
|
||
|
||
## Development
|
||
|
||
Enable debug logging:
|
||
|
||
```bash
|
||
vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \
|
||
--stage-configs-path vllm_omni/model_executor/stage_configs/qwen3_tts.yaml \
|
||
--omni \
|
||
--port 8091 \
|
||
--trust-remote-code \
|
||
--enforce-eager \
|
||
--uvicorn-log-level debug
|
||
```
|