Repository navigation
Allow configuring keepalive_ping_timeout_seconds for tts.stream_websocket (AsyncFishAudio) #47
Description
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_websocketinternally 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_wsraises aWebSocketNetworkError, 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
- Is
keepalive_ping_timeout_secondsintended to be user-configurable for long-running TTS streams? - Would you be open to a PR that adds an optional parameter (or configuration mechanism) to control this timeout without patching the library source?
- 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
twangodev commentedon Nov 17, 2025
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_wsas 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 anWebSocketNetworkErrorby 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
WebSocketOptionsor something similar. You can expect this inv1.1.0, which we can release once merged.stackpiles-naka commentedon Nov 18, 2025
@twangodev
Thanks a lot for the quick reply and for working on exposing WebSocket options –
WebSocketOptionsin 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:
AsyncFishAudiowithtts.stream_websocketin a long-running conversational app (Japanese TTS).WebSocketNetworkErrorfromhttpx_wswhile waiting onws.receive_bytes()inside TTS streaming.twangodev commentedon Nov 18, 2025
@stackpiles-naka, v1.1.0 is now available on PyPI, thanks again for opening this issue
stackpiles-naka commentedon Nov 18, 2025
@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!