Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 0 additions & 2 deletions api-reference/sdk/python/client.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

Main Fish Audio client classes.

Install the Python SDK first: `pip install fish-audio-sdk`.

<a id="fishaudio.client.FishAudio"></a>

## FishAudio Objects
Expand Down Expand Up @@ -39,7 +37,7 @@
#### \_\_init\_\_

```python
def __init__(*,

Check warning on line 40 in api-reference/sdk/python/client.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/client.mdx#L40

Did you really mean '__init__'?
api_key: Optional[str] = None,
base_url: str = "https://api.fish.audio",
timeout: float = 240.0,
Expand All @@ -57,7 +55,7 @@

<a id="fishaudio.client.FishAudio.tts"></a>

#### tts

Check warning on line 58 in api-reference/sdk/python/client.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/client.mdx#L58

Did you really mean 'tts'?

```python
@property
Expand All @@ -68,7 +66,7 @@

<a id="fishaudio.client.FishAudio.asr"></a>

#### asr

Check warning on line 69 in api-reference/sdk/python/client.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/client.mdx#L69

Did you really mean 'asr'?

```python
@property
Expand Down Expand Up @@ -163,7 +161,7 @@

<a id="fishaudio.client.AsyncFishAudio.tts"></a>

#### tts

Check warning on line 164 in api-reference/sdk/python/client.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/client.mdx#L164

Did you really mean 'tts'?

```python
@property
Expand All @@ -174,7 +172,7 @@

<a id="fishaudio.client.AsyncFishAudio.asr"></a>

#### asr

Check warning on line 175 in api-reference/sdk/python/client.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/client.mdx#L175

Did you really mean 'asr'?

```python
@property
Expand Down
105 changes: 73 additions & 32 deletions api-reference/sdk/python/core.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
<a id="fishaudio.core.omit"></a>

# fishaudio.core.omit

OMIT sentinel for distinguishing None from not-provided parameters.

Comment on lines +1 to +6

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add required frontmatter to both API-reference pages.

Both pages begin with generated anchors. Neither page declares the required YAML title and description.

  • api-reference/sdk/python/core.mdx#L1-L6: Add frontmatter before <a id="fishaudio.core.omit"></a>.
  • api-reference/sdk/python/utils.mdx#L1-L5: Add frontmatter before <a id="fishaudio.utils.stream"></a>.

As per coding guidelines, do not skip frontmatter on any MDX file. Include a clear title and description in the YAML frontmatter.

📍 Affects 2 files
  • api-reference/sdk/python/core.mdx#L1-L6 (this comment)
  • api-reference/sdk/python/utils.mdx#L1-L5
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api-reference/sdk/python/core.mdx` around lines 1 - 6, Add YAML frontmatter
with clear title and description before the generated anchor in
api-reference/sdk/python/core.mdx (lines 1-6) and
api-reference/sdk/python/utils.mdx (lines 1-5). Ensure both MDX pages retain
their existing anchors and comply with the required frontmatter convention.

Source: Coding guidelines

<a id="fishaudio.core.request_options"></a>

# fishaudio.core.request\_options

Request-level options for API calls.

<a id="fishaudio.core.request_options.RequestOptions"></a>

## RequestOptions Objects

```python
class RequestOptions()
```

Options that can be provided on a per-request basis to override client defaults.

**Attributes**:

- `timeout` - Override the client's default timeout (in seconds)
- `max_retries` - Override the client's default max retries
- `additional_headers` - Additional headers to include in the request
- `additional_query_params` - Additional query parameters to include

<a id="fishaudio.core.request_options.RequestOptions.get_timeout"></a>

Check warning on line 30 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L30

Did you really mean 'get_timeout'?

#### get\_timeout

```python
def get_timeout() -> Optional[httpx.Timeout]
```

Convert timeout to httpx.Timeout if set.

<a id="fishaudio.core.client_wrapper"></a>

# fishaudio.core.client\_wrapper

HTTP client wrapper for managing requests and authentication.

Install the Python SDK first: `pip install fish-audio-sdk`.

<a id="fishaudio.core.client_wrapper.BaseClientWrapper"></a>

## BaseClientWrapper Objects
Expand All @@ -16,7 +53,7 @@

Base wrapper with shared logic for sync/async clients.

<a id="fishaudio.core.client_wrapper.BaseClientWrapper.get_headers"></a>

Check warning on line 56 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L56

Did you really mean 'get_headers'?

#### get\_headers

Expand Down Expand Up @@ -151,38 +188,48 @@

Close the HTTP client.

<a id="fishaudio.core.request_options"></a>
<a id="fishaudio.core._ws_utils"></a>

# fishaudio.core.request\_options
# fishaudio.core.\_ws\_utils

Request-level options for API calls.
Internal helpers for synchronous httpx-ws sessions.

<a id="fishaudio.core.request_options.RequestOptions"></a>
<a id="fishaudio.core._ws_utils.drain_reader"></a>

Check warning on line 197 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L197

Did you really mean 'drain_reader'?

## RequestOptions Objects
#### drain\_reader

```python
class RequestOptions()
def drain_reader(ws: Any) -> None
```

Options that can be provided on a per-request basis to override client defaults.

**Attributes**:

- `timeout` - Override the client's default timeout (in seconds)
- `max_retries` - Override the client's default max retries
- `additional_headers` - Additional headers to include in the request
- `additional_query_params` - Additional query parameters to include

<a id="fishaudio.core.request_options.RequestOptions.get_timeout"></a>

#### get\_timeout

```python
def get_timeout() -> Optional[httpx.Timeout]
```

Convert timeout to httpx.Timeout if set.
Send the close frame, shut the socket down, and wait for httpx-ws's reader
thread to exit before the session's own ``close()`` frees the fd.

Check warning on line 206 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L206

Did you really mean 'fd'?

Why this is needed (Linux only in practice):

httpx-ws's sync ``WebSocketSession`` reads on a background thread and closes the
socket from the calling thread. A read that has *not started yet* is harmless:
CPython's ``SSLSocket._real_close`` sets ``_sslobj = None`` before closing the

Check warning on line 212 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L212

Did you really mean 'CPython's'?
fd, so a later read falls through to ``socket.recv`` on fd ``-1`` and fails with

Check warning on line 213 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L213

Did you really mean 'fd'?
``EBADF``. The problem is the read that is *already in flight*. On Linux,
``close()`` does not interrupt a thread blocked in ``recv()``. OpenSSL (with
read_ahead off) fetches a TLS record header and body with separate ``read(fd)``

Check warning on line 216 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L216

Did you really mean 'read_ahead'?
calls on the fd number cached in its BIO, so when the server's close-frame reply

Check warning on line 217 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L217

Did you really mean 'fd'?
arrives the second ``read()`` can land on a brand-new connection that has since
reused the same fd number, and that new session fails with
``[SSL: WRONG_VERSION_NUMBER]`` or a similar TLS error. macOS wakes the blocked
reader with ``EBADF`` on ``close()``, so it is unaffected.

The fix is to end the in-flight read *before* the fd is freed: send the close

Check warning on line 223 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L223

Did you really mean 'fd'?
frame (so the server still sees a clean 1000 close rather than 1006), then
``shutdown(SHUT_RDWR)`` the socket, which wakes the blocked reader with EOF, and
join the reader thread. The later ``close()`` sees ``LOCAL_CLOSING`` and skips
sending a second frame.

Residual risk: if httpx-ws has already closed the socket on one of its own error
paths (keepalive ping timeout, or a sender ``WriteError`` triggering ``close()``),

Check warning on line 230 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L230

Did you really mean 'keepalive'?
the fd is already freed, ``shutdown()`` fails with ``EBADF``, and the in-flight

Check warning on line 231 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L231

Did you really mean 'fd'?
read can still race. That can only be fixed inside httpx-ws's ``close()``.

<a id="fishaudio.core.iterators"></a>

Expand Down Expand Up @@ -223,7 +270,7 @@
#### \_\_init\_\_

```python
def __init__(iterator: Iterator[bytes])

Check warning on line 273 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L273

Did you really mean '__init__'?
```

Initialize the audio iterator wrapper.
Expand All @@ -237,7 +284,7 @@
#### \_\_iter\_\_

```python
def __iter__() -> Iterator[bytes]

Check warning on line 287 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L287

Did you really mean '__iter__'?
```

Allow direct iteration over audio chunks.
Expand Down Expand Up @@ -302,7 +349,7 @@
#### \_\_init\_\_

```python
def __init__(async_iterator: AsyncIterator[bytes])

Check warning on line 352 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L352

Did you really mean '__init__'?
```

Initialize the async audio iterator wrapper.
Expand All @@ -316,7 +363,7 @@
#### \_\_aiter\_\_

```python
def __aiter__() -> AsyncIterator[bytes]

Check warning on line 366 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L366

Did you really mean '__aiter__'?
```

Allow direct async iteration over audio chunks.
Expand Down Expand Up @@ -364,7 +411,7 @@

Options for configuring WebSocket connections.

These options are passed directly to httpx_ws's connect_ws/aconnect_ws functions.

Check warning on line 414 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L414

Did you really mean 'httpx_ws's'?
For complete documentation, see https://frankie567.github.io/httpx-ws/reference/httpx_ws/

**Attributes**:
Expand All @@ -384,9 +431,9 @@

**Notes**:

Parameter descriptions adapted from httpx_ws documentation.

Check warning on line 434 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L434

Did you really mean 'httpx_ws'?

<a id="fishaudio.core.websocket_options.WebSocketOptions.to_httpx_ws_kwargs"></a>

Check warning on line 436 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L436

Did you really mean 'to_httpx_ws_kwargs'?

#### to\_httpx\_ws\_kwargs

Expand All @@ -394,11 +441,5 @@
def to_httpx_ws_kwargs() -> dict[str, Any]
```

Convert to kwargs dict for httpx_ws aconnect_ws/connect_ws.

Check warning on line 444 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L444

Did you really mean 'kwargs'?

Check warning on line 444 in api-reference/sdk/python/core.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/core.mdx#L444

Did you really mean 'httpx_ws'?

<a id="fishaudio.core.omit"></a>

# fishaudio.core.omit

OMIT sentinel for distinguishing None from not-provided parameters.

112 changes: 60 additions & 52 deletions api-reference/sdk/python/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
[![PyPI version](https://img.shields.io/pypi/v/fish-audio-sdk.svg)](https://badge.fury.io/py/fish-audio-sdk)
[![Python Version](https://img.shields.io/badge/python-3.9+-blue)](https://pypi.org/project/fish-audio-sdk/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/fish-audio-sdk)](https://pypi.org/project/fish-audio-sdk/)
[![codecov](https://img.shields.io/codecov/c/github/fishaudio/fish-audio-python)](https://codecov.io/gh/fishaudio/fish-audio-python)

Check warning on line 14 in api-reference/sdk/python/overview.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

api-reference/sdk/python/overview.mdx#L14

Did you really mean 'codecov'?
[![License](https://img.shields.io/github/license/fishaudio/fish-audio-python)](https://github.com/fishaudio/fish-audio-python/blob/main/LICENSE)

The official Python library for the Fish Audio API
Expand Down Expand Up @@ -39,7 +39,7 @@

## Authentication

Get your API key from [Get Started](https://fish.audio/app/developers/get-started/?utm_source=docs&utm_medium=python-sdk&utm_content=get-api-key):
Get your API key from [fish.audio/app/api-keys](https://fish.audio/app/api-keys/?utm_source=github&utm_medium=sdk-readme&utm_content=python):

```bash
export FISH_API_KEY=your_api_key_here
Expand All @@ -55,8 +55,6 @@

## Quick Start

Most examples on this page pass `model="s2.1-pro-free"`, the free model, so they work before you add API credit. Without `model=`, the SDK uses a paid model.

**Synchronous:**

```python
Expand All @@ -66,7 +64,7 @@
client = FishAudio()

# Generate audio
audio = client.tts.convert(text="Hello, world!", model="s2.1-pro-free")
audio = client.tts.convert(text="Hello, world!")

# Play or save
play(audio)
Expand All @@ -82,7 +80,7 @@

async def main():
client = AsyncFishAudio()
audio = await client.tts.convert(text="Hello, world!", model="s2.1-pro-free")
audio = await client.tts.convert(text="Hello, world!")
play(audio)
save(audio, "output.mp3")

Expand All @@ -93,14 +91,35 @@

### Text-to-Speech

**Selecting a model:**

```python
# Recommended for production
production_audio = client.tts.convert(
text="Production speech",
model="s2.1-pro",
)

# Free to use, including on a new account with no API credit
free_audio = client.tts.convert(
text="Free speech",
model="s2.1-pro-free",
)

# Drama 3: our most expressive model (uses API credit)
drama_audio = client.tts.convert(
text="Expressive speech",
model="drama-3",
)
```

**With custom voice:**

```python
# Use a specific voice by ID
audio = client.tts.convert(
text="Custom voice",
reference_id="9a9cf47702da476aa4629e2506d4a857",
model="s2.1-pro-free",
reference_id="802e3bc2b27e49c2995d23ef70e6ac89"
)
```

Expand All @@ -109,8 +128,7 @@
```python
audio = client.tts.convert(
text="Speaking faster!",
speed=1.5, # 1.5x speed
model="s2.1-pro-free",
speed=1.5 # 1.5x speed
)
```

Expand All @@ -127,47 +145,51 @@
)

# Reuse across generations
audio1 = client.tts.convert(text="First message", config=config, model="s2.1-pro-free")
audio2 = client.tts.convert(text="Second message", config=config, model="s2.1-pro-free")
audio1 = client.tts.convert(text="First message", config=config)
audio2 = client.tts.convert(text="Second message", config=config)
```

**Chunk-by-chunk processing:**

```python
# Stream and process chunks as they arrive
for chunk in client.tts.stream(text="Long content...", model="s2.1-pro-free"):
for chunk in client.tts.stream(text="Long content..."):
send_to_websocket(chunk)

# Or collect all chunks
audio = client.tts.stream(text="Hello!", model="s2.1-pro-free").collect()
audio = client.tts.stream(text="Hello!").collect()
```

[Learn more](https://docs.fish.audio/features/text-to-speech)
[Learn more](https://docs.fish.audio/developer-guide/sdk-guide/python/text-to-speech)

### Speech-to-Text

```python
from fishaudio.core import RequestOptions

# Transcribe audio with transcribe-1-pro (recommended).
# Without the model header, the request runs on transcribe-1.
with open("audio.wav", "rb") as f:
result = client.asr.transcribe(
audio=f.read(),
language="en",
request_options=RequestOptions(
additional_headers={"model": "transcribe-1-pro"}
),
)
# transcribe-1-pro is the recommended model. Select it with the model header;
# without it, the request runs on transcribe-1.
pro = RequestOptions(additional_headers={"model": "transcribe-1-pro"})

with open("meeting.wav", "rb") as f:
result = client.asr.transcribe(audio=f.read(), request_options=pro)

# On transcribe-1-pro, text keeps <|speaker:N|> markers; speaker_turns has clean text per speaker.
print(result.text)

# Access timestamped segments
# Word-level timestamps
for segment in result.segments:
print(f"[{segment.start:.2f}s - {segment.end:.2f}s] {segment.text}")

# Speaker turns (transcribe-1-pro, returned with timestamps)
for turn in result.speaker_turns or []:
print(f"[{turn.start:.2f}s - {turn.end:.2f}s] {turn.speaker}: {turn.text}")

# Language of the transcript, when the API returns it
print(result.language_code) # e.g. "en"
```

[Learn more](https://docs.fish.audio/features/speech-to-text)
[Learn more](https://docs.fish.audio/developer-guide/sdk-guide/python/speech-to-text)

### Real-time Streaming

Expand All @@ -181,33 +203,23 @@
yield "this is "
yield "streaming!"

audio_stream = client.tts.stream_websocket(text_chunks(), latency="balanced", model="s2.1-pro-free")
audio_stream = client.tts.stream_websocket(text_chunks(), latency="balanced")
play(audio_stream)
```

**Asynchronous:**

```python
import asyncio
from fishaudio import AsyncFishAudio

async def text_chunks():
yield "Hello, "
yield "this is "
yield "streaming!"

async def main():
async with AsyncFishAudio() as client:
# stream_websocket is an async generator: iterate it, don't await the call
audio_stream = client.tts.stream_websocket(text_chunks(), latency="balanced", model="s2.1-pro-free")
with open("out.mp3", "wb") as f:
async for chunk in audio_stream:
f.write(chunk)

asyncio.run(main())
audio_stream = await client.tts.stream_websocket(text_chunks(), latency="balanced")
play(audio_stream)
```

[Learn more](https://docs.fish.audio/features/realtime-streaming)
[Learn more](https://docs.fish.audio/developer-guide/sdk-guide/python/websocket)
Comment on lines 213 to +222

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate files =="
git ls-files | rg '(^|/)api-reference/sdk/python/(overview|resources)\.mdx$|(^|/)src/|^packages/|pyproject\.toml|README' || true

echo
echo "== overview snippet =="
sed -n '170,210p' api-reference/sdk/python/overview.mdx

echo
echo "== resources around method docs =="
sed -n '540,590p' api-reference/sdk/python/resources.mdx

echo
echo "== search stream_websocket occurrences =="
rg -n "stream_websocket|AsyncTTSClient|tts\.stream_websocket|for .* stream_websocket|await .* stream_websocket" api-reference/sdk/python . --glob '!**/.git/**' | head -200

Repository: fishaudio/docs

Length of output: 12956


🌐 Web query:

Fish.audio SDK Python AsyncTTSClient stream_websocket example async for await documentation

💡 Result:

To perform real-time, token-by-token text-to-speech streaming with the Fish.audio Python SDK using AsyncTTSClient (accessed via AsyncFishAudio), you should use the stream_websocket method [1][2]. The stream_websocket method is an asynchronous generator [1][3]. You do not await the call itself; instead, you iterate over it using an async for loop [1][2]. This allows you to process or play back each audio chunk as soon as it is received from the server [1][4]. Example usage: import asyncio from fishaudio import AsyncFishAudio async def text_chunks: # This async generator yields text tokens as they become available yield "Hello, " yield "this is " yield "streaming via WebSocket!" async def main: async with AsyncFishAudio as client: # stream_websocket is an async generator — iterate it, don't await the call audio_stream = client.tts.stream_websocket( text_chunks, reference_id="YOUR_VOICE_ID", latency="balanced") async for chunk in audio_stream: # Handle the incoming audio chunk (e.g., write to file or stream to audio device) print(f"Received {len(chunk)} bytes of audio") # with open("out.mp3", "ab") as f: # f.write(chunk) asyncio.run(main) Key implementation details: 1. Input Generator: You must provide an iterable (or async iterable) that yields text tokens [1][4]. 2. Streaming Iteration: Use async for chunk in client.tts.stream_websocket(...) to consume the audio chunks [4][2]. 3. Lifecycle: The SDK handles the underlying WebSocket lifecycle (frames, connection, etc.) automatically [1][4]. 4. Async Clients: Ensure you are using AsyncFishAudio for asynchronous operations, as FishAudio is for synchronous usage [1][5]. For further details, refer to the official Fish Audio documentation on WebSocket streaming [1] and the SDK cookbook [4].

Citations:


🌐 Web query:

site:docs.fish.audio/developer-guide/sdk-guide/python/websocket "async for chunk" "stream_websocket"

💡 Result:

The async for loop is the standard pattern in Python for consuming asynchronous streams, such as those provided by WebSocket clients [1]. When working with WebSocket APIs that support streaming (like text-to-speech or real-time data feeds), the client typically provides an asynchronous generator that yields chunks as they arrive over the network [1]. To use this pattern, you define an asynchronous loop that iterates over the response stream. This allows your application to process or forward each chunk immediately without waiting for the entire stream to complete [1]. Example Implementation: The following example demonstrates how to consume a stream from an asynchronous WebSocket client [1]: async def main: client = AsyncFishAudio # Define an async generator to provide input tokens/data async def data_source: async for item in your_async_source: yield item # Use async for to iterate over the WebSocket stream as chunks arrive async for chunk in client.tts.stream_websocket(data_source): # Process each chunk as it arrives process(chunk) asyncio.run(main) Key implementation details: - Async Generator: The method (e.g., stream_websocket) is designed as an asynchronous generator [1]. - Non-blocking: By using async for, your program remains responsive, allowing other tasks to run while waiting for new chunks to arrive from the WebSocket connection [1]. - Protocol Handling: While high-level SDKs often handle framing (such as MessagePack or JSON) automatically, direct WebSocket implementations require manual management of message framing and connection state if not using a library-provided method [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== websocket docs =="
for f in api-reference/sdk/python/overview.mdx developer-guide/sdk-guide/python/websocket.mdx; do
  if [ -f "$f" ]; then
    echo "-- $f --"
    sed -n '1,240p' "$f"
  fi
done

echo
echo "== realtime streaming docs sync/async snippets =="
sed -n '180,210p' features/realtime-streaming.mdx || true
sed -n '50,75p' developer-guide/best-practices/real-time-streaming.mdx || true
sed -n '135,155p' developer-guide/best-practices/real-time-streaming.mdx || true

Repository: fishaudio/docs

Length of output: 8648


Use async iteration for stream_websocket in the async example.

features/realtime-streaming.mdx states that async clients expose the same streaming methods and explicitly shows async for chunk in client.tts.stream_websocket(...). resources.mdx also documents the same method as an async iterator, so api-reference/sdk/python/overview.mdx should not await client.tts.stream_websocket(...) before play(audio_stream); align it with the async async for pattern shown elsewhere.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api-reference/sdk/python/overview.mdx` around lines 188 - 197, Update the
async example around text_chunks to consume client.tts.stream_websocket(...)
with async iteration instead of awaiting it before play(audio_stream). Align the
example with the documented async iterator pattern and preserve the existing
streaming behavior.


### Voice Cloning

Expand All @@ -223,8 +235,7 @@
references=[ReferenceAudio(
audio=f.read(),
text="Text spoken in reference"
)],
model="s2.1-pro-free",
)]
)
```

Expand All @@ -246,7 +257,7 @@
)
```

[Learn more](https://docs.fish.audio/features/voice-cloning)
[Learn more](https://docs.fish.audio/developer-guide/sdk-guide/python/voice-cloning)

## Resource Clients

Expand All @@ -263,23 +274,20 @@
from fishaudio.exceptions import (
AuthenticationError,
RateLimitError,
NotFoundError,
APIError,
FishAudioError,
ValidationError,
FishAudioError
)

try:
audio = client.tts.convert(text="Hello!", model="s2.1-pro-free")
audio = client.tts.convert(text="Hello!")
except AuthenticationError:
print("Invalid API key")
except RateLimitError:
print("Rate limit exceeded")
except NotFoundError:
print("Voice model not found")
except APIError as e:
print(f"API error {e.status}: {e.message}") # any other HTTP error, including 422 validation
except ValidationError as e:
print(f"Invalid request: {e}")
except FishAudioError as e:
print(f"SDK error: {e}")
print(f"API error: {e}")
```

## Resources
Expand Down
Loading
Loading