Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/fishaudio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from ._version import __version__
from .client import AsyncFishAudio, FishAudio
from .core.iterators import AsyncAudioStream, AudioStream
from .core.websocket_options import WebSocketOptions
from .exceptions import (
APIError,
AuthenticationError,
Expand All @@ -41,7 +42,7 @@
ValidationError,
WebSocketError,
)
from .types import FlushEvent, TextEvent
from .types import FlushEvent, ReferenceAudio, TextEvent, TTSConfig
from .utils import play, save, stream

# Main exports
Expand All @@ -56,8 +57,12 @@
# Audio streams
"AudioStream",
"AsyncAudioStream",
# Configuration
"TTSConfig",
"WebSocketOptions",
# Types
"FlushEvent",
"ReferenceAudio",
"TextEvent",
# Exceptions
"APIError",
Expand Down
2 changes: 2 additions & 0 deletions src/fishaudio/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
from .client_wrapper import AsyncClientWrapper, ClientWrapper
from .omit import OMIT
from .request_options import RequestOptions
from .websocket_options import WebSocketOptions

__all__ = [
"AsyncClientWrapper",
"ClientWrapper",
"OMIT",
"RequestOptions",
"WebSocketOptions",
]
42 changes: 42 additions & 0 deletions src/fishaudio/core/websocket_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""WebSocket-level options for WebSocket connections."""

from typing import Any, Dict, Optional


class WebSocketOptions:
"""
Options that can be provided to configure WebSocket connections.

Attributes:
keepalive_ping_timeout_seconds: Maximum time to wait for a pong response
to a keepalive ping before considering the connection dead (default: 20s)
keepalive_ping_interval_seconds: Interval between keepalive pings (default: 20s)
max_message_size_bytes: Maximum size for incoming messages (default: 65,536 bytes)
queue_size: Size of the message receive queue (default: 512)
"""

def __init__(
self,
*,
keepalive_ping_timeout_seconds: Optional[float] = None,
keepalive_ping_interval_seconds: Optional[float] = None,
max_message_size_bytes: Optional[int] = None,
queue_size: Optional[int] = None,
):
self.keepalive_ping_timeout_seconds = keepalive_ping_timeout_seconds
self.keepalive_ping_interval_seconds = keepalive_ping_interval_seconds
self.max_message_size_bytes = max_message_size_bytes
self.queue_size = queue_size

def to_httpx_ws_kwargs(self) -> Dict[str, Any]:
"""Convert to kwargs dict for httpx_ws aconnect_ws/connect_ws."""
kwargs = {}
if self.keepalive_ping_timeout_seconds is not None:
kwargs["keepalive_ping_timeout_seconds"] = self.keepalive_ping_timeout_seconds
if self.keepalive_ping_interval_seconds is not None:
kwargs["keepalive_ping_interval_seconds"] = self.keepalive_ping_interval_seconds
if self.max_message_size_bytes is not None:
kwargs["max_message_size_bytes"] = self.max_message_size_bytes
if self.queue_size is not None:
kwargs["queue_size"] = self.queue_size
return kwargs
12 changes: 11 additions & 1 deletion src/fishaudio/resources/tts.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from httpx_ws import AsyncWebSocketSession, WebSocketSession, aconnect_ws, connect_ws

from .realtime import aiter_websocket_audio, iter_websocket_audio
from ..core import AsyncClientWrapper, ClientWrapper, RequestOptions
from ..core import AsyncClientWrapper, ClientWrapper, RequestOptions, WebSocketOptions
from ..core.iterators import AsyncAudioStream, AudioStream
from ..types import (
AudioFormat,
Expand Down Expand Up @@ -215,6 +215,7 @@ def stream_websocket(
config: TTSConfig = TTSConfig(),
model: Model = "s1",
max_workers: int = 10,
ws_options: Optional[WebSocketOptions] = None,
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ws_options parameter is missing from the method's docstring Args section. Please add documentation for this parameter to help users understand how to configure WebSocket connection options.

Copilot uses AI. Check for mistakes.
) -> Iterator[bytes]:
"""
Stream text and receive audio in real-time via WebSocket.
Expand Down Expand Up @@ -305,6 +306,9 @@ def text_generator():
speed, base=config.prosody
)

# Prepare WebSocket connection kwargs
ws_kwargs = ws_options.to_httpx_ws_kwargs() if ws_options else {}

executor = ThreadPoolExecutor(max_workers=max_workers)

try:
Expand All @@ -316,6 +320,7 @@ def text_generator():
"model": model,
"Authorization": f"Bearer {self._client.api_key}",
},
**ws_kwargs,
) as ws:

def sender():
Expand Down Expand Up @@ -502,6 +507,7 @@ async def stream_websocket(
speed: Optional[float] = None,
config: TTSConfig = TTSConfig(),
model: Model = "s1",
ws_options: Optional[WebSocketOptions] = None,
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ws_options parameter is missing from the method's docstring Args section. Please add documentation for this parameter to help users understand how to configure WebSocket connection options.

Copilot uses AI. Check for mistakes.
):
"""
Stream text and receive audio in real-time via WebSocket (async).
Expand Down Expand Up @@ -591,11 +597,15 @@ async def text_generator():
speed, base=config.prosody
)

# Prepare WebSocket connection kwargs
ws_kwargs = ws_options.to_httpx_ws_kwargs() if ws_options else {}

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

async def sender():
Expand Down
Loading