Run Qwen3-TTS with CUDA graph acceleration on NVIDIA DGX Spark (ARM64 / SM 121 / CUDA 13) as an OpenAI-compatible TTS API server. - Dockerfile targeting nvidia/cuda:13.0.2-base-ubuntu24.04 with ARM64 cu130 PyTorch wheels - Patch for max-seq-len support to handle long reference audio without crashes - OpenWebUI + SillyTavern compatible API endpoints (/v1/models, /v1/audio/voices, /speakers) - Voice management: auto-generate voices.json from speaker reference audio files - Auto-transcription helper script for generating reference text from audio Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
58 lines
1.7 KiB
Python
58 lines
1.7 KiB
Python
"""
|
|
Wrapper around faster-qwen3-tts's openai_server.py that injects additional
|
|
API endpoints for compatibility with OpenWebUI and SillyTavern.
|
|
|
|
Endpoints added:
|
|
GET /v1/models - Lists available voices (OpenWebUI primary discovery)
|
|
GET /v1/audio/voices - Lists available voices (OpenWebUI fallback)
|
|
GET /v1/audio/models - Lists available voices (OpenWebUI fallback)
|
|
GET /speakers - Lists speaker IDs (SillyTavern)
|
|
OPTIONS /{path} - Pre-flight CORS handler
|
|
"""
|
|
|
|
import sys
|
|
import json
|
|
from fastapi.responses import JSONResponse
|
|
|
|
# Point Python to the app directory inside the container
|
|
sys.path.append("/app/examples")
|
|
import openai_server
|
|
|
|
# Load generated voices
|
|
try:
|
|
with open('/config/voices.json', 'r') as f:
|
|
voices_data = json.load(f)
|
|
except FileNotFoundError:
|
|
voices_data = {}
|
|
|
|
# Build reusable response payloads
|
|
_voice_list = [{'id': v, 'object': 'model', 'created': 1686935002, 'owned_by': 'qwen'} for v in voices_data.keys()]
|
|
_models_response = {'object': 'list', 'data': _voice_list}
|
|
|
|
# OpenWebUI model discovery (primary)
|
|
@openai_server.app.get('/v1/models')
|
|
async def list_models():
|
|
return _models_response
|
|
|
|
# OpenWebUI voice discovery fallbacks
|
|
@openai_server.app.get('/v1/audio/voices')
|
|
async def list_audio_voices():
|
|
return _models_response
|
|
|
|
@openai_server.app.get('/v1/audio/models')
|
|
async def list_audio_models():
|
|
return _models_response
|
|
|
|
# SillyTavern speaker endpoint
|
|
@openai_server.app.get('/speakers')
|
|
async def get_speakers():
|
|
return list(voices_data.keys())
|
|
|
|
# Pre-flight OPTIONS handler to prevent 404s
|
|
@openai_server.app.options('/{path:path}')
|
|
async def options_handler(path: str):
|
|
return JSONResponse(content={'status': 'ok'})
|
|
|
|
if __name__ == '__main__':
|
|
openai_server.main()
|