Fix voice_clone/streaming backends and POST /v1/audio/voices (port from -1 repo)

- voice_clone and streaming now return active library voices instead of
  probing upstream /v1/models (which only exposed 8 model entries)
- Accept POST on /v1/audio/voices for clients that probe with POST
- Fall back to first active voice when voice field missing in /v1/audio/speech

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-06-09 02:23:43 +02:00
parent c3b1cab75b
commit 8ce84e5523

View File

@ -254,7 +254,7 @@ def _active_library_voice_options(settings: dict) -> list[dict]:
def _fetch_backend_voices(settings: dict, backend: str) -> list: def _fetch_backend_voices(settings: dict, backend: str) -> list:
backend = _clean_preview_backend(backend) backend = _clean_preview_backend(backend)
if backend in {"nvidia_zeroshot", "nvidia_flow"}: if backend in {"voice_clone", "streaming", "nvidia_zeroshot", "nvidia_flow"}:
return _active_library_voice_options(settings) return _active_library_voice_options(settings)
tts_url = _validate_http_url(_preview_backend_base_url(settings, backend), allow_private=True).rstrip("/") tts_url = _validate_http_url(_preview_backend_base_url(settings, backend), allow_private=True).rstrip("/")
key = (settings.get("voice_design_api_key") if backend == "voice_design" else settings.get("tts_api_key")) or "" key = (settings.get("voice_design_api_key") if backend == "voice_design" else settings.get("tts_api_key")) or ""
@ -683,7 +683,7 @@ async def openai_audio_models_proxy():
return await openai_models_proxy() return await openai_models_proxy()
@router.get("/v1/audio/voices") @router.api_route("/v1/audio/voices", methods=["GET", "POST"])
async def openai_audio_voices_proxy(): async def openai_audio_voices_proxy():
models = await openai_models_proxy() models = await openai_models_proxy()
return [m["id"] for m in models["data"]] return [m["id"] for m in models["data"]]
@ -706,15 +706,17 @@ async def openai_speech_proxy(request: Request):
text=text, error="input is required", text=text, error="input is required",
) )
raise HTTPException(400, "input is required") raise HTTPException(400, "input is required")
if not voice:
_routing_log_request(
request, status="error", app=request_app, requested_voice=original_voice,
routed_voice=voice, backend="", route=None, response_format=response_format,
text=text, error="voice is required",
)
raise HTTPException(400, "voice is required")
settings = _load_settings() settings = _load_settings()
if not voice:
fallback = next((v["id"] for v in _active_library_voice_options(settings)), "")
if not fallback:
_routing_log_request(
request, status="error", app=request_app, requested_voice=original_voice,
routed_voice=voice, backend="", route=None, response_format=response_format,
text=text, error="voice is required",
)
raise HTTPException(400, "voice is required")
voice = fallback
voice, route = _resolve_tts_route(request_app, voice, text) voice, route = _resolve_tts_route(request_app, voice, text)
backend = _route_backend(route, voice) backend = _route_backend(route, voice)
style_instruction = str(data.get("instruct") or data.get("style_instruction") or "") style_instruction = str(data.get("instruct") or data.get("style_instruction") or "")