OpenAI TTS Plugin Returns “no audio frames were pushed” with OpenAI-Compatible Endpoints (Kokoro + DeepInfra)

Environment

  • livekit-agents: 1.6.0
  • Python: 3.11 (uv-managed .venv)
  • Running both:
    • console mode
    • start + connect --room test-room (real RoomIO)
  • OS: Fedora Linux
  • Kokoro-FastAPI running locally on http://localhost:8880/v1

Goal

Use livekit.plugins.openai.TTS with:

  1. Local Kokoro-FastAPI (OpenAI-compatible endpoint)
  2. DeepInfra Kokoro inference TTS endpoint (OpenAI-compatible base_url)

TTS Configuration

tts=openai.TTS(

    model="kokoro",

    voice="af_alloy",

    api_key="not-needed",

    base_url="http://localhost:8880/v1",

    response_format="wav",

)

For DeepInfra:

tts=openai.TTS(

    model="MODEL_NAME",

    api_key=os.getenv("DEEPINFRA_API_KEY"),

    base_url="https://api.deepinfra.com/v1",

)

No streaming= argument (not supported in 1.6.0).

Observed Error

Consistently receiving:

livekit.agents._exceptions.APIError:

no audio frames were pushed for text: <text>


Logs show:

"streamed": false

The HTTP request does not fail. There are no 4xx or 5xx errors.

Verification Performed

1. Kokoro endpoint works via curl

curl -X POST http://localhost:8880/v1/audio/speech \

-H "Content-Type: application/json" \

-H "Authorization: Bearer not-needed" \

-d '{

    "model": "kokoro",

    "input": "Hello test",

    "voice": "af_alloy",

    "response_format": "wav"

  }' \

--output test.wav

Result:

  • HTTP/1.1 200 OK
  • Content-Type: audio/wav
  • Transfer-Encoding: chunked
  • Valid 16-bit PCM WAV @ 24000 Hz
  • file test.wav confirms valid audio

2. Same failure with DeepInfra

Using DeepInfra API key and base_url, same error:

no audio frames were pushed

3. Upgraded Agents

Upgraded from 1.5.17 → 1.6.0.

Same behavior.

4. Tested in both modes

  • console mode → fails
  • Real RTC (start + connect) → fails

So this is not console-only behavior.

Hypothesis

It appears that:

  • The OpenAI TTS plugin successfully sends the HTTP request.

  • The endpoint returns valid WAV and PCM audio.

  • However, the plugin’s stream adapter never emits decoded audio frames into the LiveKit audio pipeline.

  • This triggers the internal guard that raises:

    APIError: no audio frames were pushed
    

This suggests one of:

  1. The plugin expects a slightly different OpenAI /v1/audio/speech response contract (e.g., specific headers, framing, content encoding).
  2. The adapter requires a streaming behavior that Kokoro-FastAPI and DeepInfra are not implementing exactly as OpenAI does.
  3. The WAV returned is valid but not being parsed by the internal TTS stream adapter.

Questions

  1. Is livekit.plugins.openai.TTS guaranteed to work with third-party OpenAI-compatible /v1/audio/speech endpoints?
  2. Are there documented requirements for:
    • Content-Type
    • Transfer-Encoding
    • Response framing
    • Streaming semantics
  3. Is there a minimal known-working OpenAI-compatible TTS server implementation that can be used as a reference?
  4. Is there a recommended way to integrate local TTS models (like Kokoro) without relying on the OpenAI compatibility layer?

Any clarification on the expected /v1/audio/speech contract would be extremely helpful. Or if someone has experimented similar issues.

I really find this weird, since kokoro is listed in LiveKit’s Docs as a plugin you can use easily. Kokoro TTS plugin guide | LiveKit Documentation

PS: I also tried PCM as format and recieve the same error.

Update: Protocol Mismatch with OpenAI Endpoints & Potential Workaround

Hey everyone, dropping a quick update after looking closer at the codebase.

The Root Cause

The issue isn’t the network architecture. Recent versions of livekit.plugins.openai enforce a very strict protocol contract (specific streaming headers and response framing) tailored exactly to OpenAI. OpenAI-compatible engines like Kokoro-FastAPI and DeepInfra don’t mirror these niche semantics perfectly down to the byte layer. Because of this strictness, LiveKit’s stream adapter silently drops the audio bytes, triggering the no audio frames were pushed error.

A Potential Solution (Looking for confirmation)

Bypassing the OpenAI plugin entirely seems to be a possible solution. Theoretically, creating a custom class that inherits from tts.ChunkedStream, capturing the raw PCM bytes via an openai.AsyncClient, and feeding them directly into the pipeline using a synchronous output_emitter.push() should bypass the strict OpenAI header checks.

I haven’t fully implemented this yet—has anyone already built a working class for this, or found a definitive solution they can share?

Feature Request

Writing boilerplate adapters for an open-source model defeats the plug-and-play magic of the SDK. Since Kokoro is heavily used for low-cost, low-TTFT architectures, it would be incredible if LiveKit offered a native livekit.plugins.kokoro plugin out of the box.