Resolve #3620140 "Add Qwen3-TTS-12Hz-1.7B-CustomVoice support"
Qwen3-TTS-12Hz-1.7B-CustomVoice is documented as a Text-to-Speech model ("Text to audio"), but text_to_speech was not an implemented operation type in this provider — it was absent from getSupportedOperationTypes() and textToSpeech() threw "not implemented". This adds the operation type along with the model.
POST /v1/audio/speech was confirmed live (401 against a calibrating 404 on a nonsense path). OpenAiBasedProviderClientBase already implements TextToSpeechInterface, so no interface declaration and no composer.json constraint bump were needed.
Changes
text_to_speechadded togetSupportedOperationTypes().textToSpeech()implemented, replacing the throw.getModels()switch: filter/^(qwen3-tts)/i, replacing the commented-out OpenAI/^(tts)/i.- Registered as the
text_to_speechdefault model ingetSetupData(). definitions/api_defaults.yml: the block still carried OpenAI's voices (alloy,ash, …) and the unsupportedaacformat. Replaced with mittwald's nine voices,aacdropped,languageandspeed(0.25–4.0) added.- README: section flipped from
⏸️ to✅ , plus voices, formats, a usage example and a configuration subsection. .cspell-project-words.txt:aiden,sohee.
Two API quirks handled explicitly
- The response carries an
audio/mpegheader regardless of the requested format, sospeechResponseType()derives the MIME type and extension from the request. The inherited base implementation hardcodes mp3, which would mislabel every non-mp3 response. - The configuration form submits every field it renders, including untouched ones, so an unset
languagearrives as''. Since an invalidvoice/language/instructionsreturns a genericGot bad request.that does not name the parameter, empty optionals are stripped before the request.
Verification
verify_class.php: class declares cleanly,TextToSpeechInterfacesatisfied.verify_filters.php: the model matchestext_to_speechand nothing else; no other model's row changed (no prefix over-match).- GrumPHP / phpcs pass.
- Not run: the end-to-end AI API Explorer walkthrough, which needs a running site and a live API key.
Out of scope
Pre-existing drift noticed but deliberately not fixed here: Qwen3.5-0.8B is offered for image vision despite being text-only, and moderation is advertised while its filter matches only OpenAI IDs.