diff --git a/api-reference/sdk/python/client.mdx b/api-reference/sdk/python/client.mdx index ea7ac07..1819c9c 100644 --- a/api-reference/sdk/python/client.mdx +++ b/api-reference/sdk/python/client.mdx @@ -4,8 +4,6 @@ Main Fish Audio client classes. -Install the Python SDK first: `pip install fish-audio-sdk`. - ## FishAudio Objects diff --git a/api-reference/sdk/python/core.mdx b/api-reference/sdk/python/core.mdx index d44a7ae..dd5a279 100644 --- a/api-reference/sdk/python/core.mdx +++ b/api-reference/sdk/python/core.mdx @@ -1,11 +1,48 @@ + + +# fishaudio.core.omit + +OMIT sentinel for distinguishing None from not-provided parameters. + + + +# fishaudio.core.request\_options + +Request-level options for API calls. + + + +## 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 + + + +#### get\_timeout + +```python +def get_timeout() -> Optional[httpx.Timeout] +``` + +Convert timeout to httpx.Timeout if set. + # fishaudio.core.client\_wrapper HTTP client wrapper for managing requests and authentication. -Install the Python SDK first: `pip install fish-audio-sdk`. - ## BaseClientWrapper Objects @@ -151,38 +188,48 @@ async def close() -> None Close the HTTP client. - + -# fishaudio.core.request\_options +# fishaudio.core.\_ws\_utils -Request-level options for API calls. +Internal helpers for synchronous httpx-ws sessions. - + -## 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 - - - -#### 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. + +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 +fd, so a later read falls through to ``socket.recv`` on fd ``-1`` and fails with +``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)`` +calls on the fd number cached in its BIO, so when the server's close-frame reply +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 +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()``), +the fd is already freed, ``shutdown()`` fails with ``EBADF``, and the in-flight +read can still race. That can only be fixed inside httpx-ws's ``close()``. @@ -396,9 +443,3 @@ def to_httpx_ws_kwargs() -> dict[str, Any] Convert to kwargs dict for httpx_ws aconnect_ws/connect_ws. - - -# fishaudio.core.omit - -OMIT sentinel for distinguishing None from not-provided parameters. - diff --git a/api-reference/sdk/python/overview.mdx b/api-reference/sdk/python/overview.mdx index 2be95a1..97cb7ac 100644 --- a/api-reference/sdk/python/overview.mdx +++ b/api-reference/sdk/python/overview.mdx @@ -39,7 +39,7 @@ pip install fish-audio-sdk[utils] ## 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 @@ client = FishAudio(api_key="your_api_key") ## 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 @@ from fishaudio.utils import play, save 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 @@ from fishaudio.utils import play, save 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 @@ asyncio.run(main()) ### 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 @@ audio = client.tts.convert( ```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 @@ config = TTSConfig( ) # 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 @@ def text_chunks(): 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) ### Voice Cloning @@ -223,8 +235,7 @@ with open("reference.wav", "rb") as f: references=[ReferenceAudio( audio=f.read(), text="Text spoken in reference" - )], - model="s2.1-pro-free", + )] ) ``` @@ -246,7 +257,7 @@ audio = client.tts.convert( ) ``` -[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 @@ audio = client.tts.convert( 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 diff --git a/api-reference/sdk/python/resources.mdx b/api-reference/sdk/python/resources.mdx index c616f00..018a229 100644 --- a/api-reference/sdk/python/resources.mdx +++ b/api-reference/sdk/python/resources.mdx @@ -1,511 +1,390 @@ - - -# fishaudio.resources.voices + -Voice management namespace client. +# fishaudio.resources.asr -Install the Python SDK first: `pip install fish-audio-sdk`. +ASR (Automatic Speech Recognition) namespace client. - + -## VoicesClient Objects +## ASRClient Objects ```python -class VoicesClient() +class ASRClient() ``` -Synchronous voice management operations. +Synchronous ASR operations. - + -#### list +#### transcribe ```python -def list( - *, - page_size: int = 10, - page_number: int = 1, - title: Optional[str] = OMIT, - tags: Optional[Union[list[str], str]] = OMIT, - self_only: bool = False, - author_id: Optional[str] = OMIT, - language: Optional[Union[list[str], str]] = OMIT, - title_language: Optional[Union[list[str], str]] = OMIT, - sort_by: str = "task_count", - request_options: Optional[RequestOptions] = None -) -> PaginatedResponse[Voice] +def transcribe( + *, + audio: bytes, + language: Optional[str] = OMIT, + include_timestamps: bool = True, + request_options: Optional[RequestOptions] = None) -> ASRResponse ``` -List available voices/models. +Transcribe audio to text. **Arguments**: -- `page_size` - Number of results per page -- `page_number` - Page number (1-indexed) -- `title` - Filter by title -- `tags` - Filter by tags (single tag or list) -- `self_only` - Only return user's own voices -- `author_id` - Filter by author ID -- `language` - Filter by language(s) -- `title_language` - Filter by title language(s) -- `sort_by` - Sort field ("task_count" or "created_at") +- `audio` - Audio file bytes +- `language` - Language code (e.g., "en", "zh"). Auto-detected if not provided. +- `include_timestamps` - Whether to include timestamp information for segments + (and speaker turns, on models that return them) - `request_options` - Request-level overrides **Returns**: - Paginated response with total count and voice items + ASRResponse with transcription text, duration (in seconds), and segments, + plus language, request ID and speaker turns when the API returns them **Example**: ```python + from fishaudio.core import RequestOptions + client = FishAudio(api_key="...") - # List all voices - voices = client.voices.list(page_size=20) - print(f"Total: {voices.total}") - for voice in voices.items: - print(f"{voice.title}: {voice.id}") + with open("audio.mp3", "rb") as f: + audio_bytes = f.read() - # Filter by tags - tagged = client.voices.list(tags=["male", "english"]) + result = client.asr.transcribe( + audio=audio_bytes, + request_options=RequestOptions( + additional_headers={"model": "transcribe-1-pro"} + ), + ) + print(result.text) + for segment in result.segments: + print(f"{segment.start}-{segment.end}: {segment.text}") ``` - + -#### get +## AsyncASRClient Objects ```python -def get(voice_id: str, +class AsyncASRClient() +``` + +Asynchronous ASR operations. + + + +#### transcribe + +```python +async def transcribe( *, - request_options: Optional[RequestOptions] = None) -> Voice + audio: bytes, + language: Optional[str] = OMIT, + include_timestamps: bool = True, + request_options: Optional[RequestOptions] = None) -> ASRResponse ``` -Get voice by ID. +Transcribe audio to text (async). **Arguments**: -- `voice_id` - Voice model ID +- `audio` - Audio file bytes +- `language` - Language code (e.g., "en", "zh"). Auto-detected if not provided. +- `include_timestamps` - Whether to include timestamp information for segments + (and speaker turns, on models that return them) - `request_options` - Request-level overrides **Returns**: - Voice model details + ASRResponse with transcription text, duration (in seconds), and segments, + plus language, request ID and speaker turns when the API returns them **Example**: ```python - client = FishAudio(api_key="...") - voice = client.voices.get("voice_id_here") - print(voice.title, voice.description) - ``` - - + from fishaudio.core import RequestOptions -#### create + client = AsyncFishAudio(api_key="...") -```python -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 -``` + async with aiofiles.open("audio.mp3", "rb") as f: + audio_bytes = await f.read() -Create/clone a new voice. + result = await client.asr.transcribe( + audio=audio_bytes, + request_options=RequestOptions( + additional_headers={"model": "transcribe-1-pro"} + ), + ) + print(result.text) + for segment in result.segments: + print(f"{segment.start}-{segment.end}: {segment.text}") + ``` -**Arguments**: + -- `title` - Voice model name -- `voices` - List of audio file bytes for training -- `description` - Voice description -- `texts` - Transcripts for voice samples -- `tags` - Tags for categorization -- `cover_image` - Cover image bytes -- `visibility` - Visibility setting (public, unlist, private) -- `train_mode` - Training mode (currently only "fast" supported) -- `enhance_audio_quality` - Whether to enhance audio quality -- `request_options` - Request-level overrides - +# fishaudio.resources.tts -**Returns**: +TTS (Text-to-Speech) namespace client. - Created voice model - + -**Example**: +## TTSClient Objects - ```python - client = FishAudio(api_key="...") +```python +class TTSClient() +``` - with open("voice1.wav", "rb") as f1, open("voice2.wav", "rb") as f2: - voice = client.voices.create( - title="My Voice", - voices=[f1.read(), f2.read()], - description="Custom voice clone", - tags=["custom", "english"] - ) - print(f"Created: {voice.id}") - ``` +Synchronous TTS operations. - + -#### update +#### stream ```python -def update(voice_id: str, - *, - title: Optional[str] = OMIT, - description: Optional[str] = OMIT, - cover_image: Optional[bytes] = OMIT, - visibility: Optional[Visibility] = OMIT, - tags: Optional[builtins.list[str]] = OMIT, - request_options: Optional[RequestOptions] = None) -> None +def stream(*, + text: str, + 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", + request_options: Optional[RequestOptions] = None) -> AudioStream ``` -Update voice metadata. +Stream text-to-speech audio chunks. **Arguments**: -- `voice_id` - Voice model ID -- `title` - New title -- `description` - New description -- `cover_image` - New cover image bytes -- `visibility` - New visibility setting -- `tags` - New tags +- `text` - Text to synthesize +- `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 - `request_options` - Request-level overrides +**Returns**: + + AudioStream object that can be iterated for audio chunks + + **Example**: ```python + from fishaudio import FishAudio + client = FishAudio(api_key="...") - client.voices.update( - "voice_id_here", - title="Updated Title", - visibility="public" - ) + + # Stream and process chunks + for chunk in client.tts.stream(text="Hello world"): + process_audio_chunk(chunk) + + # Or collect all at once + audio = client.tts.stream(text="Hello world").collect() ``` - + -#### delete +#### convert ```python -def delete(voice_id: str, - *, - request_options: Optional[RequestOptions] = None) -> None +def convert(*, + text: str, + 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", + request_options: Optional[RequestOptions] = None) -> bytes ``` -Delete a voice. +Convert text to speech and return complete audio as bytes. + +This is a convenience method that streams all audio chunks and combines them. +For chunk-by-chunk processing, use stream() instead. **Arguments**: -- `voice_id` - Voice model ID +- `text` - Text to synthesize +- `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 - `request_options` - Request-level overrides +**Returns**: + + Complete audio as bytes + + **Example**: ```python + from fishaudio import FishAudio + from fishaudio.utils import play, save + client = FishAudio(api_key="...") - client.voices.delete("voice_id_here") - ``` - - -## AsyncVoicesClient Objects - -```python -class AsyncVoicesClient() -``` - -Asynchronous voice management operations. - - - -#### list - -```python -async def list( - *, - page_size: int = 10, - page_number: int = 1, - title: Optional[str] = OMIT, - tags: Optional[Union[list[str], str]] = OMIT, - self_only: bool = False, - author_id: Optional[str] = OMIT, - language: Optional[Union[list[str], str]] = OMIT, - title_language: Optional[Union[list[str], str]] = OMIT, - sort_by: str = "task_count", - request_options: Optional[RequestOptions] = None -) -> PaginatedResponse[Voice] -``` - -List available voices/models (async). See sync version for details. - - - -#### get - -```python -async def get(voice_id: str, - *, - request_options: Optional[RequestOptions] = None) -> Voice -``` - -Get voice by ID (async). See sync version for details. - - - -#### create - -```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 -``` - -Create/clone a new voice (async). See sync version for details. - - - -#### update - -```python -async def update(voice_id: str, - *, - title: Optional[str] = OMIT, - description: Optional[str] = OMIT, - cover_image: Optional[bytes] = OMIT, - visibility: Optional[Visibility] = OMIT, - tags: Optional[builtins.list[str]] = OMIT, - request_options: Optional[RequestOptions] = None) -> None -``` - -Update voice metadata (async). See sync version for details. - - - -#### delete - -```python -async def delete(voice_id: str, - *, - request_options: Optional[RequestOptions] = None) -> None -``` - -Delete a voice (async). See sync version for details. - - - -# fishaudio.resources.account - -Account namespace client for billing and credits. - - - -## AccountClient Objects - -```python -class AccountClient() -``` - -Synchronous account operations. - - - -#### get\_credits - -```python -def get_credits(*, - check_free_credit: Optional[bool] = OMIT, - request_options: Optional[RequestOptions] = None) -> Credits -``` - -Get API credit balance. - -**Arguments**: - -- `check_free_credit` - Whether to check free credit availability -- `request_options` - Request-level overrides - - -**Returns**: - - Credits information - - -**Example**: - - ```python - client = FishAudio(api_key="...") - credits = client.account.get_credits() - print(f"Available credits: {float(credits.credit)}") - - # Check free credit availability - credits = client.account.get_credits(check_free_credit=True) - if credits.has_free_credit: - print("Free credits available!") - ``` - - - -#### get\_package - -```python -def get_package(*, - request_options: Optional[RequestOptions] = None) -> Package -``` - -Get package information. - -**Arguments**: - -- `request_options` - Request-level overrides - - -**Returns**: - - Package information - + # Get complete audio + audio = client.tts.convert(text="Hello world") -**Example**: + # Play it + play(audio) - ```python - client = FishAudio(api_key="...") - package = client.account.get_package() - print(f"Balance: {package.balance}/{package.total}") + # Or save it + save(audio, "output.mp3") ``` - - -## AsyncAccountClient Objects - -```python -class AsyncAccountClient() -``` - -Asynchronous account operations. - - + -#### get\_credits +#### stream\_websocket ```python -async def get_credits( +def stream_websocket( + text_stream: Iterable[Union[str, TextEvent, FlushEvent]], *, - check_free_credit: Optional[bool] = OMIT, - request_options: Optional[RequestOptions] = None) -> Credits + 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] ``` -Get API credit balance (async). +Stream text and receive audio in real-time via WebSocket. + +Perfect for conversational AI, live captioning, and streaming applications. **Arguments**: -- `check_free_credit` - Whether to check free credit availability -- `request_options` - Request-level overrides +- `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**: - Credits information + Iterator of audio bytes **Example**: ```python - client = AsyncFishAudio(api_key="...") - credits = await client.account.get_credits() - print(f"Available credits: {float(credits.credit)}") - - # Check free credit availability - credits = await client.account.get_credits(check_free_credit=True) - if credits.has_free_credit: - print("Free credits available!") - ``` - - - -#### get\_package + from fishaudio import FishAudio, TTSConfig, ReferenceAudio, WebSocketOptions -```python -async def get_package(*, - request_options: Optional[RequestOptions] = None - ) -> Package -``` + client = FishAudio(api_key="...") -Get package information (async). + def text_generator(): + yield "Hello, " + yield "this is " + yield "streaming text!" -**Arguments**: + # 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) -- `request_options` - Request-level overrides - + # 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) -**Returns**: + # 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) - Package information - + # 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) -**Example**: + # 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) - ```python - client = AsyncFishAudio(api_key="...") - package = await client.account.get_package() - print(f"Balance: {package.balance}/{package.total}") + # 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) ``` - - -# fishaudio.resources.tts - -TTS (Text-to-Speech) namespace client. - - + -## TTSClient Objects +## AsyncTTSClient Objects ```python -class TTSClient() +class AsyncTTSClient() ``` -Synchronous TTS operations. +Asynchronous TTS operations. - + #### stream ```python -def stream(*, - text: str, - 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 = "s2-pro", - request_options: Optional[RequestOptions] = None) -> AudioStream +async def stream( + *, + text: str, + 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", + request_options: Optional[RequestOptions] = None) -> AsyncAudioStream ``` -Stream text-to-speech audio chunks. +Stream text-to-speech audio chunks (async). **Arguments**: @@ -522,42 +401,43 @@ Stream text-to-speech audio chunks. **Returns**: - AudioStream object that can be iterated for audio chunks + AsyncAudioStream object that can be iterated for audio chunks **Example**: ```python - from fishaudio import FishAudio + from fishaudio import AsyncFishAudio - client = FishAudio(api_key="...") + client = AsyncFishAudio(api_key="...") # Stream and process chunks - for chunk in client.tts.stream(text="Hello world"): - process_audio_chunk(chunk) + async for chunk in await client.tts.stream(text="Hello world"): + await process_audio_chunk(chunk) # Or collect all at once - audio = client.tts.stream(text="Hello world").collect() + stream = await client.tts.stream(text="Hello world") + audio = await stream.collect() ``` - + #### convert ```python -def convert(*, - text: str, - 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 = "s2-pro", - request_options: Optional[RequestOptions] = None) -> bytes +async def convert(*, + text: str, + 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", + request_options: Optional[RequestOptions] = None) -> bytes ``` -Convert text to speech and return complete audio as bytes. +Convert text to speech and return complete audio as bytes (async). This is a convenience method that streams all audio chunks and combines them. For chunk-by-chunk processing, use stream() instead. @@ -583,13 +463,13 @@ For chunk-by-chunk processing, use stream() instead. **Example**: ```python - from fishaudio import FishAudio + from fishaudio import AsyncFishAudio from fishaudio.utils import play, save - client = FishAudio(api_key="...") + client = AsyncFishAudio(api_key="...") # Get complete audio - audio = client.tts.convert(text="Hello world") + audio = await client.tts.convert(text="Hello world") # Play it play(audio) @@ -598,32 +478,31 @@ For chunk-by-chunk processing, use stream() instead. save(audio, "output.mp3") ``` - + #### stream\_websocket ```python -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: Model = "s2-pro", - max_workers: int = 10, - ws_options: Optional[WebSocketOptions] = None) -> Iterator[bytes] +async def stream_websocket(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: Union[Model, str] = "s2.1-pro", + ws_options: Optional[WebSocketOptions] = None) ``` -Stream text and receive audio in real-time via WebSocket. +Stream text and receive audio in real-time via WebSocket (async). Perfect for conversational AI, live captioning, and streaming applications. **Arguments**: -- `text_stream` - Iterator of text chunks to stream +- `text_stream` - Async 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) @@ -631,7 +510,6 @@ Perfect for conversational AI, live captioning, and streaming applications. - `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. @@ -639,460 +517,598 @@ Perfect for conversational AI, live captioning, and streaming applications. **Returns**: - Iterator of audio bytes + Async iterator of audio bytes **Example**: ```python - from fishaudio import FishAudio, TTSConfig, ReferenceAudio, WebSocketOptions + from fishaudio import AsyncFishAudio, TTSConfig, ReferenceAudio, WebSocketOptions - client = FishAudio(api_key="...") + client = AsyncFishAudio(api_key="...") - def text_generator(): + async def text_generator(): yield "Hello, " yield "this is " - yield "streaming text!" + yield "async streaming!" # 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) + async with aiofiles.open("output.mp3", "wb") as f: + async for audio_chunk in client.tts.stream_websocket(text_generator()): + await f.write(audio_chunk) # With format and speed parameters - with open("output.wav", "wb") as f: - for audio_chunk in client.tts.stream_websocket( + async with aiofiles.open("output.wav", "wb") as f: + async for audio_chunk in client.tts.stream_websocket( text_generator(), format="wav", speed=1.3 ): - f.write(audio_chunk) + await 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) + async with aiofiles.open("output.mp3", "wb") as f: + async for audio_chunk in client.tts.stream_websocket(text_generator(), reference_id="your_model_id"): + await f.write(audio_chunk) # With references parameter - with open("output.mp3", "wb") as f: - for audio_chunk in client.tts.stream_websocket( + async with aiofiles.open("output.mp3", "wb") as f: + async for audio_chunk in client.tts.stream_websocket( text_generator(), references=[ReferenceAudio(audio=audio_bytes, text="sample")] ): - f.write(audio_chunk) + await 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) + async with aiofiles.open("output.mp3", "wb") as f: + async for audio_chunk in client.tts.stream_websocket( + text_generator(), + ws_options=ws_options + ): + await f.write(audio_chunk) + + # Parameters override config values + config = TTSConfig(format="mp3", latency="balanced") + async with aiofiles.open("output.wav", "wb") as f: + async for audio_chunk in client.tts.stream_websocket( + text_generator(), + format="wav", # Parameter wins + config=config + ): + await f.write(audio_chunk) + ``` + + + +# fishaudio.resources.realtime + +Real-time WebSocket streaming helpers. + + + +#### iter\_websocket\_audio + +```python +def iter_websocket_audio(ws) -> Iterator[bytes] +``` + +Process WebSocket audio messages (sync). + +Receives messages from WebSocket, yields audio chunks, handles errors. +Unknown events are ignored and iteration continues. + +**Arguments**: + +- `ws` - WebSocket connection from httpx_ws.connect_ws + + +**Yields**: + + Audio bytes + + +**Raises**: + +- `WebSocketError` - On disconnect or error finish event + + + +#### aiter\_websocket\_audio + +```python +async def aiter_websocket_audio(ws) -> AsyncIterator[bytes] +``` + +Process WebSocket audio messages (async). + +Receives messages from WebSocket, yields audio chunks, handles errors. +Unknown events are ignored and iteration continues. + +**Arguments**: + +- `ws` - WebSocket connection from httpx_ws.aconnect_ws + + +**Yields**: + + Audio bytes + + +**Raises**: + +- `WebSocketError` - On disconnect or error finish event + + + +# fishaudio.resources.account + +Account namespace client for billing and credits. + + + +## AccountClient Objects + +```python +class AccountClient() +``` + +Synchronous account operations. + + + +#### get\_credits + +```python +def get_credits(*, + check_free_credit: Optional[bool] = OMIT, + request_options: Optional[RequestOptions] = None) -> Credits +``` + +Get API credit balance. + +**Arguments**: + +- `check_free_credit` - Whether to check free credit availability +- `request_options` - Request-level overrides + + +**Returns**: + + Credits information + + +**Example**: + + ```python + client = FishAudio(api_key="...") + credits = client.account.get_credits() + print(f"Available credits: {float(credits.credit)}") + + # Check free credit availability + credits = client.account.get_credits(check_free_credit=True) + if credits.has_free_credit: + print("Free credits available!") + ``` + + + +#### get\_package + +```python +def get_package(*, + request_options: Optional[RequestOptions] = None) -> Package +``` + +Get package information. + +**Arguments**: + +- `request_options` - Request-level overrides + - # 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) +**Returns**: - # 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) + Package information + + +**Example**: + + ```python + client = FishAudio(api_key="...") + package = client.account.get_package() + print(f"Balance: {package.balance}/{package.total}") ``` - + -## AsyncTTSClient Objects +## AsyncAccountClient Objects ```python -class AsyncTTSClient() +class AsyncAccountClient() ``` -Asynchronous TTS operations. +Asynchronous account operations. - + -#### stream +#### get\_credits ```python -async def stream( +async def get_credits( *, - text: str, - 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 = "s2-pro", - request_options: Optional[RequestOptions] = None) -> AsyncAudioStream + check_free_credit: Optional[bool] = OMIT, + request_options: Optional[RequestOptions] = None) -> Credits ``` -Stream text-to-speech audio chunks (async). +Get API credit balance (async). **Arguments**: -- `text` - Text to synthesize -- `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 +- `check_free_credit` - Whether to check free credit availability - `request_options` - Request-level overrides **Returns**: - AsyncAudioStream object that can be iterated for audio chunks + Credits information **Example**: ```python - from fishaudio import AsyncFishAudio - client = AsyncFishAudio(api_key="...") + credits = await client.account.get_credits() + print(f"Available credits: {float(credits.credit)}") - # Stream and process chunks - async for chunk in await client.tts.stream(text="Hello world"): - await process_audio_chunk(chunk) - - # Or collect all at once - stream = await client.tts.stream(text="Hello world") - audio = await stream.collect() + # Check free credit availability + credits = await client.account.get_credits(check_free_credit=True) + if credits.has_free_credit: + print("Free credits available!") ``` - + -#### convert +#### get\_package ```python -async def convert(*, - text: str, - 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 = "s2-pro", - request_options: Optional[RequestOptions] = None) -> bytes +async def get_package(*, + request_options: Optional[RequestOptions] = None + ) -> Package ``` -Convert text to speech and return complete audio as bytes (async). - -This is a convenience method that streams all audio chunks and combines them. -For chunk-by-chunk processing, use stream() instead. +Get package information (async). **Arguments**: -- `text` - Text to synthesize -- `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 - `request_options` - Request-level overrides **Returns**: - Complete audio as bytes + Package information **Example**: ```python - from fishaudio import AsyncFishAudio - from fishaudio.utils import play, save - client = AsyncFishAudio(api_key="...") + package = await client.account.get_package() + print(f"Balance: {package.balance}/{package.total}") + ``` - # Get complete audio - audio = await client.tts.convert(text="Hello world") + - # Play it - play(audio) +# fishaudio.resources.voices - # Or save it - save(audio, "output.mp3") - ``` +Voice management namespace client. - + -#### stream\_websocket +## VoicesClient Objects ```python -async def stream_websocket(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 = "s2-pro", - ws_options: Optional[WebSocketOptions] = None) +class VoicesClient() ``` -Stream text and receive audio in real-time via WebSocket (async). +Synchronous voice management operations. -Perfect for conversational AI, live captioning, and streaming applications. + + +#### list + +```python +def list( + *, + page_size: int = 10, + page_number: int = 1, + title: Optional[str] = OMIT, + tags: Optional[Union[list[str], str]] = OMIT, + self_only: bool = False, + author_id: Optional[str] = OMIT, + language: Optional[Union[list[str], str]] = OMIT, + title_language: Optional[Union[list[str], str]] = OMIT, + sort_by: str = "task_count", + request_options: Optional[RequestOptions] = None +) -> PaginatedResponse[Voice] +``` + +List available voices/models. **Arguments**: -- `text_stream` - Async 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 -- `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. +- `page_size` - Number of results per page +- `page_number` - Page number (1-indexed) +- `title` - Filter by title +- `tags` - Filter by tags (single tag or list) +- `self_only` - Only return user's own voices +- `author_id` - Filter by author ID +- `language` - Filter by language(s) +- `title_language` - Filter by title language(s) +- `sort_by` - Sort field ("task_count" or "created_at") +- `request_options` - Request-level overrides **Returns**: - Async iterator of audio bytes + Paginated response with total count and voice items **Example**: ```python - from fishaudio import AsyncFishAudio, TTSConfig, ReferenceAudio, WebSocketOptions - - client = AsyncFishAudio(api_key="...") - - async def text_generator(): - yield "Hello, " - yield "this is " - yield "async streaming!" - - # Simple usage with defaults - async with aiofiles.open("output.mp3", "wb") as f: - async for audio_chunk in client.tts.stream_websocket(text_generator()): - await f.write(audio_chunk) - - # With format and speed parameters - async with aiofiles.open("output.wav", "wb") as f: - async for audio_chunk in client.tts.stream_websocket( - text_generator(), - format="wav", - speed=1.3 - ): - await f.write(audio_chunk) - - # With reference_id parameter - async with aiofiles.open("output.mp3", "wb") as f: - async for audio_chunk in client.tts.stream_websocket(text_generator(), reference_id="your_model_id"): - await f.write(audio_chunk) - - # With references parameter - async with aiofiles.open("output.mp3", "wb") as f: - async for audio_chunk in client.tts.stream_websocket( - text_generator(), - references=[ReferenceAudio(audio=audio_bytes, text="sample")] - ): - await 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) - async with aiofiles.open("output.mp3", "wb") as f: - async for audio_chunk in client.tts.stream_websocket( - text_generator(), - ws_options=ws_options - ): - await f.write(audio_chunk) - - # Parameters override config values - config = TTSConfig(format="mp3", latency="balanced") - async with aiofiles.open("output.wav", "wb") as f: - async for audio_chunk in client.tts.stream_websocket( - text_generator(), - format="wav", # Parameter wins - config=config - ): - await f.write(audio_chunk) - ``` - - + client = FishAudio(api_key="...") -# fishaudio.resources.realtime + # List all voices + voices = client.voices.list(page_size=20) + print(f"Total: {voices.total}") + for voice in voices.items: + print(f"{voice.title}: {voice.id}") -Real-time WebSocket streaming helpers. + # Filter by tags + tagged = client.voices.list(tags=["male", "english"]) + ``` - + -#### iter\_websocket\_audio +#### get ```python -def iter_websocket_audio(ws) -> Iterator[bytes] +def get(voice_id: str, + *, + request_options: Optional[RequestOptions] = None) -> Voice ``` -Process WebSocket audio messages (sync). - -Receives messages from WebSocket, yields audio chunks, handles errors. -Unknown events are ignored and iteration continues. +Get voice by ID. **Arguments**: -- `ws` - WebSocket connection from httpx_ws.connect_ws +- `voice_id` - Voice model ID +- `request_options` - Request-level overrides -**Yields**: +**Returns**: - Audio bytes + Voice model details -**Raises**: +**Example**: -- `WebSocketError` - On disconnect or error finish event + ```python + client = FishAudio(api_key="...") + voice = client.voices.get("voice_id_here") + print(voice.title, voice.description) + ``` - + -#### aiter\_websocket\_audio +#### create ```python -async def aiter_websocket_audio(ws) -> AsyncIterator[bytes] +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 ``` -Process WebSocket audio messages (async). - -Receives messages from WebSocket, yields audio chunks, handles errors. -Unknown events are ignored and iteration continues. +Create/clone a new voice. **Arguments**: -- `ws` - WebSocket connection from httpx_ws.aconnect_ws +- `title` - Voice model name +- `voices` - List of audio file bytes for training +- `description` - Voice description +- `texts` - Transcripts for voice samples +- `tags` - Tags for categorization +- `cover_image` - Cover image bytes +- `visibility` - Visibility setting (public, unlist, private) +- `train_mode` - Training mode (currently only "fast" supported) +- `enhance_audio_quality` - Whether to enhance audio quality +- `request_options` - Request-level overrides -**Yields**: +**Returns**: - Audio bytes + Created voice model -**Raises**: - -- `WebSocketError` - On disconnect or error finish event - - +**Example**: -# fishaudio.resources.asr + ```python + client = FishAudio(api_key="...") -ASR (Automatic Speech Recognition) namespace client. + with open("voice1.wav", "rb") as f1, open("voice2.wav", "rb") as f2: + voice = client.voices.create( + title="My Voice", + voices=[f1.read(), f2.read()], + description="Custom voice clone", + tags=["custom", "english"] + ) + print(f"Created: {voice.id}") + ``` - + -## ASRClient Objects +#### update ```python -class ASRClient() +def update(voice_id: str, + *, + title: Optional[str] = OMIT, + description: Optional[str] = OMIT, + cover_image: Optional[bytes] = OMIT, + visibility: Optional[Visibility] = OMIT, + tags: Optional[builtins.list[str]] = OMIT, + request_options: Optional[RequestOptions] = None) -> None ``` -Synchronous ASR operations. +Update voice metadata. - +**Arguments**: -#### transcribe +- `voice_id` - Voice model ID +- `title` - New title +- `description` - New description +- `cover_image` - New cover image bytes +- `visibility` - New visibility setting +- `tags` - New tags +- `request_options` - Request-level overrides + + +**Example**: + + ```python + client = FishAudio(api_key="...") + client.voices.update( + "voice_id_here", + title="Updated Title", + visibility="public" + ) + ``` + + + +#### delete ```python -def transcribe( - *, - audio: bytes, - language: Optional[str] = OMIT, - include_timestamps: bool = True, - request_options: Optional[RequestOptions] = None) -> ASRResponse +def delete(voice_id: str, + *, + request_options: Optional[RequestOptions] = None) -> None ``` -Transcribe audio to text. +Delete a voice. **Arguments**: -- `audio` - Audio file bytes -- `language` - Language code (e.g., "en", "zh"). Auto-detected if not provided. -- `include_timestamps` - Whether to include timestamp information for segments +- `voice_id` - Voice model ID - `request_options` - Request-level overrides -**Returns**: - - ASRResponse with transcription text, duration, and segments - - **Example**: ```python client = FishAudio(api_key="...") + client.voices.delete("voice_id_here") + ``` - with open("audio.mp3", "rb") as f: - audio_bytes = f.read() + - result = client.asr.transcribe(audio=audio_bytes, language="en") - print(result.text) - for segment in result.segments: - print(f"{segment.start}-{segment.end}: {segment.text}") - ``` +## AsyncVoicesClient Objects - +```python +class AsyncVoicesClient() +``` -## AsyncASRClient Objects +Asynchronous voice management operations. + + + +#### list ```python -class AsyncASRClient() +async def list( + *, + page_size: int = 10, + page_number: int = 1, + title: Optional[str] = OMIT, + tags: Optional[Union[list[str], str]] = OMIT, + self_only: bool = False, + author_id: Optional[str] = OMIT, + language: Optional[Union[list[str], str]] = OMIT, + title_language: Optional[Union[list[str], str]] = OMIT, + sort_by: str = "task_count", + request_options: Optional[RequestOptions] = None +) -> PaginatedResponse[Voice] ``` -Asynchronous ASR operations. +List available voices/models (async). See sync version for details. - + -#### transcribe +#### get ```python -async def transcribe( - *, - audio: bytes, - language: Optional[str] = OMIT, - include_timestamps: bool = True, - request_options: Optional[RequestOptions] = None) -> ASRResponse +async def get(voice_id: str, + *, + request_options: Optional[RequestOptions] = None) -> Voice ``` -Transcribe audio to text (async). +Get voice by ID (async). See sync version for details. -**Arguments**: + -- `audio` - Audio file bytes -- `language` - Language code (e.g., "en", "zh"). Auto-detected if not provided. -- `include_timestamps` - Whether to include timestamp information for segments -- `request_options` - Request-level overrides - +#### create -**Returns**: +```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 +``` - ASRResponse with transcription text, duration, and segments - +Create/clone a new voice (async). See sync version for details. -**Example**: + - ```python - client = AsyncFishAudio(api_key="...") +#### update - async with aiofiles.open("audio.mp3", "rb") as f: - audio_bytes = await f.read() +```python +async def update(voice_id: str, + *, + title: Optional[str] = OMIT, + description: Optional[str] = OMIT, + cover_image: Optional[bytes] = OMIT, + visibility: Optional[Visibility] = OMIT, + tags: Optional[builtins.list[str]] = OMIT, + request_options: Optional[RequestOptions] = None) -> None +``` - result = await client.asr.transcribe(audio=audio_bytes, language="en") - print(result.text) - for segment in result.segments: - print(f"{segment.start}-{segment.end}: {segment.text}") - ``` +Update voice metadata (async). See sync version for details. + + + +#### delete + +```python +async def delete(voice_id: str, + *, + request_options: Optional[RequestOptions] = None) -> None +``` + +Delete a voice (async). See sync version for details. diff --git a/api-reference/sdk/python/types.mdx b/api-reference/sdk/python/types.mdx index 3873865..64c46fc 100644 --- a/api-reference/sdk/python/types.mdx +++ b/api-reference/sdk/python/types.mdx @@ -1,124 +1,67 @@ - - -# fishaudio.types.voices - -Voice and model management types. - - - -## Sample Objects - -```python -class Sample(BaseModel) -``` - -A sample audio for a voice model. + -**Attributes**: +# fishaudio.types.asr -- `title` - Title/name of the audio sample -- `text` - Transcription of the spoken content in the sample -- `task_id` - Unique identifier for the sample task -- `audio` - URL or path to the audio file +ASR (Automatic Speech Recognition) related types. - + -## Author Objects +## ASRSegment Objects ```python -class Author(BaseModel) +class ASRSegment(BaseModel) ``` -Voice model author information. +A timestamped segment of transcribed text. **Attributes**: -- `id` - Unique author identifier -- `nickname` - Author's display name -- `avatar` - URL to author's avatar image +- `text` - The transcribed text for this segment +- `start` - Segment start time in seconds +- `end` - Segment end time in seconds - + -## Voice Objects +## SpeakerTurn Objects ```python -class Voice(BaseModel) +class SpeakerTurn(BaseModel) ``` -A voice model. - -Represents a TTS voice that can be used for synthesis. +A stretch of the transcript spoken by one speaker. **Attributes**: -- `id` - Unique voice model identifier (use as reference_id in TTS) -- `type` - Model type. Options: "svc" (singing voice conversion), "tts" (text-to-speech) -- `title` - Voice model title/name -- `description` - Detailed description of the voice model -- `cover_image` - URL to the voice model's cover image -- `train_mode` - Training mode used. Options: "fast" -- `state` - Current model state: "created", "training", "trained", or "failed" -- `tags` - List of tags for categorization (e.g., ["male", "english", "young"]) -- `samples` - List of audio samples demonstrating the voice -- `created_at` - Timestamp when the model was created -- `updated_at` - Timestamp when the model was last updated -- `languages` - List of supported language codes (e.g., ["en", "zh"]) -- `visibility` - Model visibility. Options: "public", "private", "unlist" -- `lock_visibility` - Whether visibility setting is locked -- `like_count` - Number of likes the model has received -- `mark_count` - Number of bookmarks/favorites -- `shared_count` - Number of times the model has been shared -- `task_count` - Number of times the model has been used for generation -- `liked` - Whether the current user has liked this model. Default: False -- `marked` - Whether the current user has bookmarked this model. Default: False -- `author` - Information about the voice model's creator - - - -# fishaudio.types.account - -Account-related types (credits, packages, etc.). +- `speaker` - Speaker label (e.g., "speaker:0"). Labels identify speakers within one response only +- `text` - What the speaker said in this turn, without inline speaker markers +- `start` - Turn start time in seconds +- `end` - Turn end time in seconds - + -## Credits Objects +## ASRResponse Objects ```python -class Credits(BaseModel) +class ASRResponse(BaseModel) ``` -User's API credit balance. +Response from speech-to-text transcription. **Attributes**: -- `id` - Unique credits record identifier -- `user_id` - User identifier -- `credit` - Current credit balance (decimal for precise accounting) -- `created_at` - Timestamp when the credits record was created -- `updated_at` - Timestamp when the credits were last updated -- `has_phone_sha256` - Whether the user has a verified phone number. Optional -- `has_free_credit` - Whether the user has received free credits. Optional - - - -## Package Objects - -```python -class Package(BaseModel) -``` +- `text` - Complete transcription of the entire audio +- `duration` - Total audio duration in seconds +- `segments` - List of timestamped text segments. Empty if include_timestamps=False +- `language` - Display name of the transcript's language (e.g., "English"). None if not returned +- `language_code` - Language code of the transcript (e.g., "en"): the detected language, or the requested language if none was detected. None if not returned +- `request_id` - Identifier of this request; quote it when reporting a problem with the request. None if not returned +- `speaker_turns` - Speaker turns in order, with times in seconds. Returned only by models with speaker diarization, and only if include_timestamps=True; empty if no speech was found. None otherwise -User's prepaid package information. + -**Attributes**: +#### duration -- `id` - Unique package identifier -- `user_id` - User identifier -- `type` - Package type identifier -- `total` - Total units in the package -- `balance` - Remaining units in the package -- `created_at` - Timestamp when the package was purchased -- `updated_at` - Timestamp when the package was last updated -- `finished_at` - Timestamp when the package was fully consumed. None if still active +Duration in seconds @@ -205,7 +148,7 @@ All parameters have sensible defaults. - `chunk_length` - Characters per generation chunk. Range: 100-300. Default: 200. Lower values = faster initial response, higher values = better quality - `latency` - Generation mode. Options: "normal" (higher quality), "balanced" (faster). Default: "balanced" -- `reference_id` - Voice model ID from fish.audio (e.g., "9a9cf47702da476aa4629e2506d4a857"). +- `reference_id` - Voice model ID from fish.audio (e.g., "802e3bc2b27e49c2995d23ef70e6ac89"). Find IDs in voice URLs or via voices.list() - `references` - List of reference audio samples for instant voice cloning. Default: [] - `prosody` - Speech speed and volume settings. Default: None (uses natural prosody) @@ -312,78 +255,156 @@ WebSocket event to end the streaming session. - `event` - Event type identifier, always "stop" - + -# fishaudio.types.shared +# fishaudio.types.account -Shared types used across the SDK. +Account-related types (credits, packages, etc.). - + -## PaginatedResponse Objects +## Credits Objects ```python -class PaginatedResponse(BaseModel, Generic[T]) +class Credits(BaseModel) ``` -Generic paginated response. +User's API credit balance. **Attributes**: -- `total` - Total number of items across all pages -- `items` - List of items on the current page +- `id` - Unique credits record identifier +- `user_id` - User identifier +- `credit` - Current credit balance (decimal for precise accounting) +- `created_at` - Timestamp when the credits record was created +- `updated_at` - Timestamp when the credits were last updated +- `has_phone_sha256` - Whether the user has a verified phone number. Optional +- `has_free_credit` - Whether the user has received free credits. Optional - + -#### warn\_if\_deprecated\_model +## Package Objects ```python -def warn_if_deprecated_model(model: str) -> None +class Package(BaseModel) ``` -Emit a deprecation warning if a legacy model is used. +User's prepaid package information. - +**Attributes**: -# fishaudio.types.asr +- `id` - Unique package identifier +- `user_id` - User identifier +- `type` - Package type identifier +- `total` - Total units in the package +- `balance` - Remaining units in the package +- `created_at` - Timestamp when the package was purchased +- `updated_at` - Timestamp when the package was last updated +- `finished_at` - Timestamp when the package was fully consumed. None if still active -ASR (Automatic Speech Recognition) related types. + - +# fishaudio.types.voices -## ASRSegment Objects +Voice and model management types. + + + +## Sample Objects ```python -class ASRSegment(BaseModel) +class Sample(BaseModel) ``` -A timestamped segment of transcribed text. +A sample audio for a voice model. **Attributes**: -- `text` - The transcribed text for this segment -- `start` - Segment start time in seconds -- `end` - Segment end time in seconds +- `title` - Title/name of the audio sample +- `text` - Transcription of the spoken content in the sample +- `task_id` - Unique identifier for the sample task +- `audio` - URL or path to the audio file - + -## ASRResponse Objects +## Author Objects ```python -class ASRResponse(BaseModel) +class Author(BaseModel) ``` -Response from speech-to-text transcription. +Voice model author information. **Attributes**: -- `text` - Complete transcription of the entire audio -- `duration` - Total audio duration in seconds -- `segments` - List of timestamped text segments. Empty if include_timestamps=False +- `id` - Unique author identifier +- `nickname` - Author's display name +- `avatar` - URL to author's avatar image - + -#### duration +## Voice Objects -Duration in seconds +```python +class Voice(BaseModel) +``` + +A voice model. + +Represents a TTS voice that can be used for synthesis. + +**Attributes**: + +- `id` - Unique voice model identifier (use as reference_id in TTS) +- `type` - Model type. Options: "svc" (singing voice conversion), "tts" (text-to-speech) +- `title` - Voice model title/name +- `description` - Detailed description of the voice model +- `cover_image` - URL to the voice model's cover image +- `train_mode` - Training mode used. Options: "fast" +- `state` - Current model state (e.g., "ready", "training", "failed") +- `tags` - List of tags for categorization (e.g., ["male", "english", "young"]) +- `samples` - List of audio samples demonstrating the voice +- `created_at` - Timestamp when the model was created +- `updated_at` - Timestamp when the model was last updated +- `languages` - List of supported language codes (e.g., ["en", "zh"]) +- `visibility` - Model visibility. Options: "public", "private", "unlist" +- `lock_visibility` - Whether visibility setting is locked +- `like_count` - Number of likes the model has received +- `mark_count` - Number of bookmarks/favorites +- `shared_count` - Number of times the model has been shared +- `task_count` - Number of times the model has been used for generation +- `liked` - Whether the current user has liked this model. Default: False +- `marked` - Whether the current user has bookmarked this model. Default: False +- `author` - Information about the voice model's creator + + + +# fishaudio.types.shared + +Shared types used across the SDK. + + + +## PaginatedResponse Objects + +```python +class PaginatedResponse(BaseModel, Generic[T]) +``` + +Generic paginated response. + +**Attributes**: + +- `total` - Total number of items across all pages +- `items` - List of items on the current page + + + +#### warn\_if\_deprecated\_model + +```python +def warn_if_deprecated_model(model: str) -> None +``` + +Emit a deprecation warning if a legacy model is used. diff --git a/api-reference/sdk/python/utils.mdx b/api-reference/sdk/python/utils.mdx index 93a81c5..100aa97 100644 --- a/api-reference/sdk/python/utils.mdx +++ b/api-reference/sdk/python/utils.mdx @@ -1,11 +1,47 @@ + + +# fishaudio.utils.save + +Audio saving utility. + + + +#### save + +```python +def save(audio: Union[bytes, Iterable[bytes]], filename: str) -> None +``` + +Save audio to a file. + +**Arguments**: + +- `audio` - Audio bytes or iterable of bytes +- `filename` - Path to save the audio file + + +**Examples**: + + ```python + from fishaudio import FishAudio, save + + client = FishAudio(api_key="...") + audio = client.tts.convert(text="Hello world") + + # Save to file + save(audio, "output.mp3") + + # Works with iterators too + audio_stream = client.tts.convert(text="Another example") + save(audio_stream, "another.mp3") + ``` + # fishaudio.utils.play Audio playback utility. -Install the Python SDK first: `pip install fish-audio-sdk`. - #### play @@ -49,44 +85,6 @@ Play audio using various playback methods. play(audio, use_ffmpeg=False) ``` - - -# fishaudio.utils.save - -Audio saving utility. - - - -#### save - -```python -def save(audio: Union[bytes, Iterable[bytes]], filename: str) -> None -``` - -Save audio to a file. - -**Arguments**: - -- `audio` - Audio bytes or iterable of bytes -- `filename` - Path to save the audio file - - -**Examples**: - - ```python - from fishaudio import FishAudio, save - - client = FishAudio(api_key="...") - audio = client.tts.convert(text="Hello world") - - # Save to file - save(audio, "output.mp3") - - # Works with iterators too - audio_stream = client.tts.convert(text="Another example") - save(audio_stream, "another.mp3") - ``` - # fishaudio.utils.stream