Update Python SDK API Reference - #112
Conversation
Auto-generated from fishaudio/fish-audio-python@4481509
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with 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.
Inline comments:
In `@api-reference/sdk/python/core.mdx`:
- Around line 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.
- Around line 218-220: Update the max_message_size_bytes documentation entry so
“Default: 65536 bytes (64 KiB)” remains part of its description rather than
appearing as a separate attribute; keep the queue_size entry unchanged.
In `@api-reference/sdk/python/overview.mdx`:
- Around line 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.
In `@api-reference/sdk/python/resources.mdx`:
- Around line 285-388: Update the async stream_websocket documentation examples
to consume AsyncTTSClient.stream_websocket with async for, writing or processing
each yielded audio chunk directly. Replace any await-assigned stream usage and
play(audio_stream) pattern in overview.mdx while preserving the existing
examples’ behavior and parameters.
In `@api-reference/sdk/python/utils.mdx`:
- Around line 30-37: Add an explicit mpv installation or PATH prerequisite
between the Raises and Examples sections in the stream documentation, so users
know to make mpv available before running the stream example. Preserve the
existing example and DependencyError documentation.
- Around line 37-49: Dedent the fenced Python examples in the Examples sections,
including the blocks containing FishAudio stream usage, so each opening and
closing fence starts at column 0 and the Python body is aligned without the
extra four-space indentation. Apply the same formatting to all referenced
examples while preserving their code and python language tags.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 5a034557-efc3-42c5-b709-b032c788a72b
📒 Files selected for processing (5)
api-reference/sdk/python/core.mdxapi-reference/sdk/python/overview.mdxapi-reference/sdk/python/resources.mdxapi-reference/sdk/python/types.mdxapi-reference/sdk/python/utils.mdx
| <a id="fishaudio.core.omit"></a> | ||
|
|
||
| # fishaudio.core.omit | ||
|
|
||
| OMIT sentinel for distinguishing None from not-provided parameters. | ||
|
|
There was a problem hiding this comment.
🎯 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
| - `max_message_size_bytes` - Message size in bytes to receive from the server. | ||
| - `Default` - 65536 bytes (64 KiB). | ||
| - `queue_size` - Size of the queue where received messages will be held until they |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Keep the default value under max_message_size_bytes.
Line 219 is rendered as a separate attribute named Default. Move the value into the max_message_size_bytes description.
Proposed fix
-- `max_message_size_bytes` - Message size in bytes to receive from the server.
-- `Default` - 65536 bytes (64 KiB).
+- `max_message_size_bytes` - Message size in bytes to receive from the server.
+ Default: 65536 bytes (64 KiB).📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - `max_message_size_bytes` - Message size in bytes to receive from the server. | |
| - `Default` - 65536 bytes (64 KiB). | |
| - `queue_size` - Size of the queue where received messages will be held until they | |
| - `max_message_size_bytes` - Message size in bytes to receive from the server. | |
| Default: 65536 bytes (64 KiB). | |
| - `queue_size` - Size of the queue where received messages will be held until they |
🤖 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 218 - 220, Update the
max_message_size_bytes documentation entry so “Default: 65536 bytes (64 KiB)”
remains part of its description rather than appearing as a separate attribute;
keep the queue_size entry unchanged.
| 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") | ||
| 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) |
There was a problem hiding this comment.
🗄️ 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 -200Repository: 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:
- 1: https://docs.fish.audio/developer-guide/sdk-guide/python/websocket
- 2: https://docs.fish.audio/api-reference/sdk/python/overview
- 3: https://docs.fish.audio/developer-guide/sdk-guide/python/overview
- 4: https://docs.fish.audio/developer-guide/sdk-guide/cookbook/realtime-llm-to-speech
- 5: https://github.com/fishaudio/fish-audio-python
🌐 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 || trueRepository: 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.
| <a id="fishaudio.resources.tts.TTSClient.stream_websocket"></a> | ||
|
|
||
| #### stream\_websocket | ||
|
|
||
| ```python | ||
| async def create(*, | ||
| title: str, | ||
| voices: builtins.list[bytes], | ||
| description: Optional[str] = OMIT, | ||
| texts: Optional[builtins.list[str]] = OMIT, | ||
| tags: Optional[builtins.list[str]] = OMIT, | ||
| cover_image: Optional[bytes] = OMIT, | ||
| visibility: Visibility = "private", | ||
| train_mode: str = "fast", | ||
| enhance_audio_quality: bool = True, | ||
| request_options: Optional[RequestOptions] = None) -> Voice | ||
| def stream_websocket( | ||
| text_stream: Iterable[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: Union[Model, str] = "s2.1-pro", | ||
| max_workers: int = 10, | ||
| ws_options: Optional[WebSocketOptions] = None) -> Iterator[bytes] | ||
| ``` | ||
|
|
||
| Create/clone a new voice (async). See sync version for details. | ||
| Stream text and receive audio in real-time via WebSocket. | ||
|
|
||
| <a id="fishaudio.resources.voices.AsyncVoicesClient.update"></a> | ||
| Perfect for conversational AI, live captioning, and streaming applications. | ||
|
|
||
| #### update | ||
| **Arguments**: | ||
|
|
||
| - `text_stream` - Iterator of text chunks to stream | ||
| - `reference_id` - Voice reference ID (overrides config.reference_id if provided) | ||
| - `references` - Reference audio samples (overrides config.references if provided) | ||
| - `format` - Audio format - "mp3", "wav", "pcm", or "opus" (overrides config.format if provided) | ||
| - `latency` - Latency mode - "normal" or "balanced" (overrides config.latency if provided) | ||
| - `speed` - Speech speed multiplier, e.g. 1.5 for 1.5x speed (overrides config.prosody.speed if provided) | ||
| - `config` - TTS configuration (audio settings, voice, model parameters) | ||
| - `model` - TTS model to use | ||
| - `max_workers` - ThreadPoolExecutor workers for concurrent sender | ||
| - `ws_options` - WebSocket connection options for configuring timeouts, message size limits, etc. | ||
| Useful for long-running generations that may exceed default timeout values. | ||
| See WebSocketOptions class for available parameters. | ||
|
|
||
|
|
||
| **Returns**: | ||
|
|
||
| Iterator of audio bytes | ||
|
|
||
|
|
||
| **Example**: | ||
|
|
||
| ```python | ||
| from fishaudio import FishAudio, TTSConfig, ReferenceAudio, WebSocketOptions | ||
|
|
||
| client = FishAudio(api_key="...") | ||
|
|
||
| def text_generator(): | ||
| yield "Hello, " | ||
| yield "this is " | ||
| yield "streaming text!" | ||
|
|
||
| # Simple usage with defaults | ||
| with open("output.mp3", "wb") as f: | ||
| for audio_chunk in client.tts.stream_websocket(text_generator()): | ||
| f.write(audio_chunk) | ||
|
|
||
| # With format and speed parameters | ||
| with open("output.wav", "wb") as f: | ||
| for audio_chunk in client.tts.stream_websocket( | ||
| text_generator(), | ||
| format="wav", | ||
| speed=1.3 | ||
| ): | ||
| f.write(audio_chunk) | ||
|
|
||
| # With reference_id parameter | ||
| with open("output.mp3", "wb") as f: | ||
| for audio_chunk in client.tts.stream_websocket(text_generator(), reference_id="your_model_id"): | ||
| f.write(audio_chunk) | ||
|
|
||
| # With references parameter | ||
| with open("output.mp3", "wb") as f: | ||
| for audio_chunk in client.tts.stream_websocket( | ||
| text_generator(), | ||
| references=[ReferenceAudio(audio=audio_bytes, text="sample")] | ||
| ): | ||
| f.write(audio_chunk) | ||
|
|
||
| # With WebSocket options for long-running generations | ||
| # Useful if you're generating very long responses that may take >20 seconds | ||
| ws_options = WebSocketOptions(keepalive_ping_timeout_seconds=60.0) | ||
| with open("output.mp3", "wb") as f: | ||
| for audio_chunk in client.tts.stream_websocket( | ||
| text_generator(), | ||
| ws_options=ws_options | ||
| ): | ||
| f.write(audio_chunk) | ||
|
|
||
| # Parameters override config values | ||
| config = TTSConfig(format="mp3", latency="balanced") | ||
| with open("output.wav", "wb") as f: | ||
| for audio_chunk in client.tts.stream_websocket( | ||
| text_generator(), | ||
| format="wav", # Parameter wins | ||
| config=config | ||
| ): | ||
| f.write(audio_chunk) | ||
| ``` | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate files =="
fd -a 'resources\.mdx|overview\.mdx' . | sed 's#^\./##'
echo
echo "== resources.mdx stream_websocket sections =="
file="api-reference/sdk/python/resources.mdx"
if [ -f "$file" ]; then
wc -l "$file"
sed -n '280,395p' "$file"
sed -n '500,620p' "$file"
fi
echo
echo "== overview.mdx stream_websocket/async usage =="
overview="api-reference/sdk/python/overview.mdx"
if [ -f "$overview" ]; then
rg -n "stream_websocket|async for|await client\.tts|play\\(" "$overview" -C 4
fi
echo
echo "== repo source references to AsyncTTSClient.stream_websocket =="
rg -n "class AsyncTTSClient|async def stream_websocket|def stream_websocket|AsyncTTSClient|stream_websocket" . -g '!**/build/**' -g '!**/dist/**' -g '!**/node_modules/**' | head -200Repository: fishaudio/docs
Length of output: 17427
Align the async stream_websocket examples to async iteration.
AsyncTTSClient.stream_websocket is documented as an async generator, so use async for chunk in client.tts.stream_websocket(...) and avoid audio_stream = await client.tts.stream_websocket(...) followed by play(audio_stream) in overview.mdx.
🧰 Tools
🪛 GitHub Check: Mintlify Validation (hanabiaiinc) - vale-spellcheck
[warning] 290-290: api-reference/sdk/python/resources.mdx#L290
Did you really mean 'stream_websocket'?
[warning] 313-313: api-reference/sdk/python/resources.mdx#L313
Did you really mean 'wav'?
[warning] 313-313: api-reference/sdk/python/resources.mdx#L313
Did you really mean 'pcm'?
🤖 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/resources.mdx` around lines 285 - 388, Update the
async stream_websocket documentation examples to consume
AsyncTTSClient.stream_websocket with async for, writing or processing each
yielded audio chunk directly. Replace any await-assigned stream usage and
play(audio_stream) pattern in overview.mdx while preserving the existing
examples’ behavior and parameters.
| **Raises**: | ||
|
|
||
| - `DependencyError` - If mpv is not installed | ||
|
|
||
|
|
||
| **Examples**: | ||
|
|
||
| ```python |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Files:"
git ls-files | rg '(^|/)utils\.mdx$|\.mdx$' || true
echo
echo "Target file outline (if possible):"
ast-grep outline api-reference/sdk/python/utils.mdx || true
echo
echo "Target file lines 1-120:"
cat -n api-reference/sdk/python/utils.mdx | sed -n '1,120p'
echo
echo "Search for stream function / DependencyError examples:"
rg -n "def stream|stream\(|DependencyError|mpv" api-reference/sdk/python/utils.mdx api-reference -S || trueRepository: fishaudio/docs
Length of output: 10927
Add an mpv prerequisite before the stream example.
stream() plays audio with mpv, and DependencyError is raised when mpv is not installed. Add the prerequisite before Examples so users can install mpv or make it available on PATH before running the code.
🧰 Tools
🪛 GitHub Check: Mintlify Validation (hanabiaiinc) - vale-spellcheck
[warning] 32-32: api-reference/sdk/python/utils.mdx#L32
Did you really mean 'mpv'?
🤖 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/utils.mdx` around lines 30 - 37, Add an explicit mpv
installation or PATH prerequisite between the Raises and Examples sections in
the stream documentation, so users know to make mpv available before running the
stream example. Preserve the existing example and DependencyError documentation.
Source: Coding guidelines
| ```python | ||
| from fishaudio import FishAudio, stream | ||
|
|
||
| client = FishAudio(api_key="...") | ||
| audio_stream = client.tts.convert(text="Hello world") | ||
|
|
||
| # Stream and play in real-time, get complete audio | ||
| complete_audio = stream(audio_stream) | ||
|
|
||
| # Save the captured audio | ||
| with open("output.mp3", "wb") as f: | ||
| f.write(complete_audio) | ||
| ``` |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
awk '
/^[[:space:]]*```python$/ || /^[[:space:]]*```$/ {
if ($0 !~ /^```python$/ && $0 !~ /^```$/) {
print "Indented fenced block at Line " NR
bad = 1
}
}
END { exit bad }
' api-reference/sdk/python/utils.mdxRepository: fishaudio/docs
Length of output: 352
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf 'Context for api-reference/sdk/python/utils.mdx lines 1-170:\n'
nl -ba api-reference/sdk/python/utils.mdx | sed -n '1,170p'
printf '\nMarkdown fence indentation evidence:\n'
python3 - <<'PY'
from pathlib import Path
p = Path("api-reference/sdk/python/utils.mdx")
for n, line in enumerate(p.read_text().splitlines(), 1):
stripped = line.strip()
if stripped.startswith("```"):
print(f"{n}: leading_spaces={len(line)-len(line.lstrip())!r} line={line!r}")
PYRepository: fishaudio/docs
Length of output: 252
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf 'Context for api-reference/sdk/python/utils.mdx lines 1-170:\n'
awk '{printf "%5d\t%s\n", NR, $0}' api-reference/sdk/python/utils.mdx | sed -n '1,170p'
printf '\nMarkdown fence indentation evidence:\n'
python3 - <<'PY'
from pathlib import Path
p = Path("api-reference/sdk/python/utils.mdx")
for n, line in enumerate(p.read_text().splitlines(), 1):
stripped = line.strip()
if stripped.startswith("```"):
print(f"{n}: leading_spaces={len(line)-len(line.lstrip())!r} line={line!r}")
PY
printf '\nMarkdown fence counts:\n'
python3 - <<'PY'
from pathlib import Path
p = Path("api-reference/sdk/python/utils.mdx")
fences = [line for line in p.read_text().splitlines() if line.strip().startswith("```")]
print("fence_count:", len(fences))
print("indented_fence_count:", [line for line in fences if line.startswith(" ```")])
PYRepository: fishaudio/docs
Length of output: 4292
Dedent the fenced Python examples.
api-reference/sdk/python/utils.mdx has fenced examples under **Examples** at lines 37–49, 84–98, and 124–136 that are indented by four spaces. Some Markdown/MDX parsers treat these as indented code blocks instead of language-tagged fenced blocks. Place the fences at column 0 and dedent the python bodies so the language tags render consistently.
🤖 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/utils.mdx` around lines 37 - 49, Dedent the fenced
Python examples in the Examples sections, including the blocks containing
FishAudio stream usage, so each opening and closing fence starts at column 0 and
the Python body is aligned without the extra four-space indentation. Apply the
same formatting to all referenced examples while preserving their code and
python language tags.
Source: Coding guidelines
Auto-generated API documentation update from fish-audio-python@448150989781e6f91c3ca9f6cc59711a87df8130
Changes
Summary by CodeRabbit