Repository navigation
Update Python SDK API Reference #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,7 @@ | |
| [](https://badge.fury.io/py/fish-audio-sdk) | ||
| [](https://pypi.org/project/fish-audio-sdk/) | ||
| [](https://pypi.org/project/fish-audio-sdk/) | ||
| [](https://codecov.io/gh/fishaudio/fish-audio-python) | ||
| [](https://github.com/fishaudio/fish-audio-python/blob/main/LICENSE) | ||
|
|
||
| The official Python library for the Fish Audio API | ||
|
|
@@ -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 | ||
|
|
@@ -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 | ||
|
|
@@ -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) | ||
|
|
@@ -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") | ||
|
|
||
|
|
@@ -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" | ||
| ) | ||
| ``` | ||
|
|
||
|
|
@@ -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 | ||
| ) | ||
| ``` | ||
|
|
||
|
|
@@ -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 | ||
|
|
||
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 -200Repository: fishaudio/docs Length of output: 12956 🌐 Web query:
💡 Result: To perform real-time, token-by-token text-to-speech streaming with the Fish.audio Python SDK using Citations:
🌐 Web query:
💡 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
🤖 Prompt for AI Agents |
||
|
|
||
| ### Voice Cloning | ||
|
|
||
|
|
@@ -223,8 +235,7 @@ | |
| references=[ReferenceAudio( | ||
| audio=f.read(), | ||
| text="Text spoken in reference" | ||
| )], | ||
| model="s2.1-pro-free", | ||
| )] | ||
| ) | ||
| ``` | ||
|
|
||
|
|
@@ -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 | ||
|
|
||
|
|
@@ -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 | ||
|
|
||
There was a problem hiding this comment.
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
titleanddescription.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
Source: Coding guidelines