fix(openai): read TTS response by content-type, not model name - #6930
Open
abidullahdev wants to merge 1 commit into
Open
fix(openai): read TTS response by content-type, not model name#6930abidullahdev wants to merge 1 commit into
abidullahdev wants to merge 1 commit into
Conversation
longcw
approved these changes
Aug 21, 2026
longcw
left a comment
Contributor
There was a problem hiding this comment.
looks good to me! something nit:
longcw
reviewed
Aug 21, 2026
| input_tokens=input_tokens, | ||
| output_tokens=output_tokens, | ||
| ) | ||
| media_type = stream.headers.get("content-type", "").split(";")[0].strip().lower() |
Contributor
There was a problem hiding this comment.
nit: maybe move the output_emitter.initialize() here and initialize with read mine_type, e.g.
media_type = stream.headers.get("content-type", "").split(";")[0].strip().lower()
# a server that ignored response_format still declares what it sent
mime_type = (
media_type if media_type in DECODABLE_CONTENT_TYPES else f"audio/{self._opts.response_format}"
)
output_emitter.initialize(..., mime_type=mime_type)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pointing the OpenAI TTS plugin at an OpenAI-compatible endpoint produces no audio. The request succeeds, the provider bills it, and the turn fails with
no audio frames were pushed. This has been reported twice on the forum without a root cause, most recently yesterday, so I dug into it.What happens
synthesize()picks the response parser from the model name:Anything outside those two names gets
SSEChunkedStream, which sendsstream_format="sse"and reads only lines beginning withdata:. Butstream_formatis an OpenAI extension. A compatible server ignores the unknown field and replies with the audio bytes ofresponse_format, so no line ever matches andpush()is never called.pushed_duration()is then 0, which raisesAPIError("no audio frames were pushed"). That error is retryable andmax_retrydefaults to 3, so the provider is called four times per utterance. Every call returns a valid 200 with real audio in the body, which is why this reads as a provider problem rather than a plugin one.The fix
Parse the body according to the response
Content-Typeinstead of the model name.text/event-streamis read as SSE, anything else as raw audio bytes. Since both branches now share one request and one error path, the two near-identical stream classes collapse into a singleChunkedStream.The request side is untouched:
tts-1/tts-1-hdstill ask forstream_format="audio", everything else still asks for"sse". Existing OpenAI users send byte-identical requests and take the same parsing path as before. The only behavioural difference is that a non-SSE response is now decoded rather than discarded.@darryncampbell — on the forum I said I'd omit
stream_formatfor unrecognised models. I dropped that idea while writing this.gpt-4o-mini-tts-2025-12-15isn't in either allowlist, and omitting the field would route it to the raw-audio branch, which is the only branch that doesn't call_set_token_usage(). Usage metrics would silently fall to zero for it and for every future model snapshot, with nothing to flag it. Content-Type alone fixes the reported bug and doesn't rot as the model catalogue grows. Happy to add the request-side change separately if you'd still like it.AudioChunkedStreamandSSEChunkedStreamare gone rather than aliased. Neither was in__all__and nothing in the repo imports them.Reproducing
Any compatible server does the same: DeepInfra, Kokoro-FastAPI, vLLM, LM Studio, Speaches.
curlagainst the same endpoint returns valid audio.Tests
tests/test_plugin_openai_tts.pydrives the publicTTSAPI overhttpx.MockTransport. Three of the six fail onmainwith the exact error from the reports. Serving the repo's owntests/long.mp3asaudio/mpegdecodes to 46.6s of audio with this change and raisesno audio frames were pushedwithout it.Prior reports