Skip to content

Allow configuring keepalive_ping_timeout_seconds for tts.stream_websocket (AsyncFishAudio) #47

Closed

Description

@stackpiles-naka

I'm using AsyncFishAudio with tts.stream_websocket to stream TTS audio over WebSocket in a conversational application.

When generating relatively long responses, I frequently hit a WebSocketNetworkError coming from httpx_ws during ws.receive_bytes(). After some investigation, I found that increasing keepalive_ping_timeout_seconds on the underlying aconnect_ws call from the default 20 seconds to 60 seconds completely eliminates these errors for my use case.

Right now, the only way I can change this timeout is by modifying the library source directly, which is not maintainable.

Environment

  • Library: fishaudio (AsyncFishAudio)
  • Feature: AsyncTTSClient.stream_websocket / client.tts.stream_websocket(...)
  • Transport: WebSocket via httpx_ws.aconnect_ws
  • Use case: Long-form, streamed conversational TTS (responses can be quite long)

Current behavior

  • AsyncTTSClient.stream_websocket internally calls:
 async with aconnect_ws(
     "/v1/tts/live",
     client=self._client.client,
     headers={"model": model, "Authorization": f"Bearer {self._client.api_key}"}
 ) as ws:
     ...
  • For long TTS generations, there can be periods where no audio chunks or other frames are received for more than 20 seconds.
  • In those cases, httpx_ws raises a WebSocketNetworkError, which bubbles up to my application and breaks the TTS stream.

Workaround

If I patch the library locally and change the aconnect_ws call to:

async with aconnect_ws(
    "/v1/tts/live",
    client=self._client.client,
    headers={"model": model, "Authorization": f"Bearer {self._client.api_key}"},
    keepalive_ping_timeout_seconds=60,
) as ws:

...then the WebSocketNetworkError no longer occurs, and long TTS responses stream successfully. However, this requires modifying the installed package, which is fragile and hard to maintain across upgrades.

Requested / expected behavior

It would be great if the keepalive timeout were configurable from the public API, for example by:

  • Adding an optional parameter to:
 async def stream_websocket(
     self,
     text_stream: AsyncIterable[Union[str, TextEvent, FlushEvent]],
     *,
     reference_id: Optional[str] = None,
     references: Optional[List[ReferenceAudio]] = None,
     format: Optional[AudioFormat] = None,
     latency: Optional[LatencyMode] = None,
     speed: Optional[float] = None,
     config: TTSConfig = TTSConfig(),
     model: Model = "s1",
     keepalive_ping_timeout_seconds: int | None = None,  # for example
 ):

and passing it through to aconnect_ws, with a sensible default (e.g. current behavior).

  • Or alternatively, exposing this via some configuration or RequestOptions-like object.

Questions

  1. Is keepalive_ping_timeout_seconds intended to be user-configurable for long-running TTS streams?
  2. Would you be open to a PR that adds an optional parameter (or configuration mechanism) to control this timeout without patching the library source?
  3. Is there a recommended pattern in this library for configuring WebSocket-level timeouts for TTS streaming?

Having an official way to configure this timeout would make it much easier to support long-form conversational TTS without resorting to local patches. Thanks!

Activity

  1. self-assigned this
    on Nov 17, 2025
  2. twangodev commented on Nov 17, 2025

    @twangodev
    Contributor

    Hi @stackpiles-naka ,

    Thanks for submitting the issue. I'm currently looking into this issue and I totally agree that we should allow users to add additional options which get fed down to the underlying library. I'm implementing a slightly more robust way to expose arguments to httpx_ws as you mention (along with unit/integration tests to accompany it).

    However, I'm unable to reproduce a timeout issue (with the default 20s from httpx_ws)or get an WebSocketNetworkError by yielding very large chunks. Can you share some more details or a code sample which reproduces the error?

    You can track the progress via #48, we will expose WebSocketOptions or something similar. You can expect this in v1.1.0, which we can release once merged.

  3. stackpiles-naka commented on Nov 18, 2025

    @stackpiles-naka
    Author

    @twangodev

    Thanks a lot for the quick reply and for working on exposing WebSocket options – WebSocketOptions in v1.1.0 sounds perfect for this use case.

    Regarding a reproducer: I've been trying to build a small standalone script that reproduces the issue, but so far I’m having a hard time triggering the timeout reliably outside of the real application.

    Some more context about where I see the issue in production:

    • I’m using AsyncFishAudio with tts.stream_websocket in a long-running conversational app (Japanese TTS).
    • The text stream is produced by an LLM (Gemini), so:
      • Text comes in as an async iterator of chunks over time.
      • Total responses can be several paragraphs long.
      • There can be periods where no audio chunks arrive for a while during long generations.
    • In that environment, I occasionally get WebSocketNetworkError from httpx_ws while waiting on ws.receive_bytes() inside TTS streaming.
  4. twangodev commented on Nov 18, 2025

    @twangodev
    Contributor

    @stackpiles-naka, v1.1.0 is now available on PyPI, thanks again for opening this issue

  5. stackpiles-naka commented on Nov 18, 2025

    @stackpiles-naka
    Author

    @twangodev Amazing turnaround – thank you so much for the incredibly fast fix and release of v1.1.0!

    I really appreciate how quickly you picked up the issue, this makes it much easier for us to rely on FishAudio in production.

    I’ll upgrade to v1.1.0, switch to the new options instead of my local patch, and report back if I see anything unexpected. Thanks again for the great support and responsiveness!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions