runapi-fish-audio 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: runapi-fish-audio
3
+ Version: 0.1.0
4
+ Summary: RunAPI Fish Audio SDK for speech generation in JavaScript, Python, Ruby, Go, Java, and PHP
5
+ Project-URL: Homepage, https://runapi.ai/models/fish-audio
6
+ Project-URL: Documentation, https://runapi.ai/docs#sdk-fish-audio
7
+ Project-URL: Source, https://github.com/runapi-ai/fish-audio-sdk
8
+ Project-URL: Issues, https://github.com/runapi-ai/fish-audio-sdk/issues
9
+ Project-URL: Changelog, https://github.com/runapi-ai/fish-audio-sdk/blob/main/CHANGELOG.md
10
+ Project-URL: Release Notes, https://github.com/runapi-ai/fish-audio-sdk/releases/tag/python%2Fv0.1.0
11
+ Author-email: RunAPI <contact@runapi.ai>
12
+ License-Expression: Apache-2.0
13
+ Keywords: api,audio,fish-audio,golang,java,maven,python,ruby,runapi,runapi-ai,sdk,speech,tts,typescript
14
+ Requires-Python: >=3.9
15
+ Requires-Dist: runapi-core>=0.2.0
16
+ Description-Content-Type: text/markdown
17
+
18
+ # Fish Audio Python SDK for RunAPI
19
+
20
+ Install `runapi-fish-audio`, create `FishAudioClient`, and call `client.text_to_speech.run(model="s1", text="Hello")`.
21
+
22
+ Model details and pricing: https://runapi.ai/models/fish-audio
23
+
24
+ Licensed under the Apache License, Version 2.0.
@@ -0,0 +1,7 @@
1
+ # Fish Audio Python SDK for RunAPI
2
+
3
+ Install `runapi-fish-audio`, create `FishAudioClient`, and call `client.text_to_speech.run(model="s1", text="Hello")`.
4
+
5
+ Model details and pricing: https://runapi.ai/models/fish-audio
6
+
7
+ Licensed under the Apache License, Version 2.0.
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "runapi-fish-audio"
7
+ version = "0.1.0"
8
+ description = "RunAPI Fish Audio SDK for speech generation in JavaScript, Python, Ruby, Go, Java, and PHP"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "Apache-2.0"
12
+ authors = [{ name = "RunAPI", email = "contact@runapi.ai" }]
13
+ keywords = ["runapi", "runapi-ai", "fish-audio", "tts", "speech", "audio", "api", "sdk", "typescript", "python", "ruby", "golang", "java", "maven"]
14
+ dependencies = ["runapi-core>=0.2.0"]
15
+
16
+ [project.urls]
17
+ Homepage = "https://runapi.ai/models/fish-audio"
18
+ Documentation = "https://runapi.ai/docs#sdk-fish-audio"
19
+ Source = "https://github.com/runapi-ai/fish-audio-sdk"
20
+ Issues = "https://github.com/runapi-ai/fish-audio-sdk/issues"
21
+ Changelog = "https://github.com/runapi-ai/fish-audio-sdk/blob/main/CHANGELOG.md"
22
+ "Release Notes" = "https://github.com/runapi-ai/fish-audio-sdk/releases/tag/python%2Fv0.1.0"
23
+
24
+ [tool.hatch.build.targets.wheel]
25
+ packages = ["src/runapi"]
26
+
27
+ [tool.uv]
28
+ package = true
29
+
30
+ [dependency-groups]
31
+ dev = ["pytest>=8"]
32
+
33
+ [tool.runapi]
34
+ slug = "fish-audio"
@@ -0,0 +1,20 @@
1
+ """Fish Audio client for RunAPI."""
2
+
3
+ from runapi.core import (
4
+ AuthenticationError,
5
+ InsufficientCreditsError,
6
+ NotFoundError,
7
+ RateLimitError,
8
+ ValidationError,
9
+ )
10
+
11
+ from .client import FishAudioClient
12
+
13
+ __all__ = [
14
+ "FishAudioClient",
15
+ "AuthenticationError",
16
+ "RateLimitError",
17
+ "InsufficientCreditsError",
18
+ "NotFoundError",
19
+ "ValidationError",
20
+ ]
@@ -0,0 +1,19 @@
1
+ """Fish Audio client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Optional
6
+
7
+ from runapi.core import ClientOptions, HttpClient, resolve_api_key
8
+
9
+ from .resources.text_to_speech import TextToSpeech
10
+
11
+
12
+ class FishAudioClient:
13
+ """Fish Audio speech generation client."""
14
+
15
+ def __init__(self, api_key: Optional[str] = None, **options: Any) -> None:
16
+ resolved_api_key = resolve_api_key(api_key)
17
+ client_options = ClientOptions(api_key=resolved_api_key, **options)
18
+ http = client_options.http_client or HttpClient(client_options)
19
+ self.text_to_speech = TextToSpeech(http)
@@ -0,0 +1,17 @@
1
+ CONTRACT = {
2
+ "text-to-speech": {
3
+ "models": ["s1", "s2-pro"],
4
+ "fields_by_model": {
5
+ "s1": {
6
+ "text": {
7
+ "required": True
8
+ }
9
+ },
10
+ "s2-pro": {
11
+ "text": {
12
+ "required": True
13
+ }
14
+ }
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,3 @@
1
+ from .text_to_speech import TextToSpeech
2
+
3
+ __all__ = ["TextToSpeech"]
@@ -0,0 +1,22 @@
1
+ """Fish Audio text-to-speech resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Optional
6
+
7
+ from runapi.core import RequestOptions, Resource
8
+
9
+ from ..contract_gen import CONTRACT
10
+ from ..types import TextToSpeechResponse
11
+
12
+
13
+ class TextToSpeech(Resource):
14
+ """Generate a RunAPI-managed MP3 from text."""
15
+
16
+ ENDPOINT = "/api/v1/fish_audio/text_to_speech"
17
+ RESPONSE_CLASS = TextToSpeechResponse
18
+
19
+ def run(self, options: Optional[RequestOptions] = None, **params: Any) -> Any:
20
+ compacted = self._compact_params(params)
21
+ self._validate_contract(CONTRACT["text-to-speech"], compacted)
22
+ return self._request("post", self.ENDPOINT, body=compacted, options=options)
@@ -0,0 +1,21 @@
1
+ """Fish Audio response models."""
2
+
3
+ from runapi.core import BaseModel, optional, required
4
+
5
+
6
+ class Audio(BaseModel):
7
+ """A RunAPI-managed MP3 audio result."""
8
+
9
+ url = required(str)
10
+ format = required(str)
11
+ mime_type = required(str)
12
+ size_bytes = required(int)
13
+
14
+
15
+ class TextToSpeechResponse(BaseModel):
16
+ """Completed synchronous text-to-speech response."""
17
+
18
+ id = required(str)
19
+ status = required(str)
20
+ audios = required([lambda: Audio])
21
+ error = optional(str)
@@ -0,0 +1,39 @@
1
+ import pytest
2
+
3
+ from runapi.core import config
4
+ from runapi.core.errors import ValidationError
5
+ from runapi.fish_audio import FishAudioClient
6
+ from runapi.fish_audio.types import TextToSpeechResponse
7
+
8
+
9
+ class FakeHttp:
10
+ def __init__(self, *responses):
11
+ self._responses = list(responses)
12
+ self.calls = []
13
+
14
+ def request(self, method, path, body=None, options=None):
15
+ self.calls.append((method, path, body))
16
+ return self._responses.pop(0)
17
+
18
+
19
+ @pytest.fixture(autouse=True)
20
+ def reset_config(monkeypatch):
21
+ monkeypatch.delenv("RUNAPI_API_KEY", raising=False)
22
+ monkeypatch.setattr(config, "api_key", None)
23
+
24
+
25
+ def test_run_posts_params_and_decodes_managed_audio():
26
+ fake = FakeHttp({"id": "task_1", "status": "completed", "audios": [{"url": "https://runapi.ai/audio.mp3", "format": "mp3", "mime_type": "audio/mpeg", "size_bytes": 128}]})
27
+ client = FishAudioClient(api_key="k", http_client=fake)
28
+
29
+ result = client.text_to_speech.run(model="s1", text="Hello")
30
+
31
+ assert fake.calls == [("post", "/api/v1/fish_audio/text_to_speech", {"model": "s1", "text": "Hello"})]
32
+ assert isinstance(result, TextToSpeechResponse)
33
+ assert result.audios[0].format == "mp3"
34
+
35
+
36
+ def test_run_requires_text():
37
+ client = FishAudioClient(api_key="k", http_client=FakeHttp())
38
+ with pytest.raises(ValidationError, match="text is required"):
39
+ client.text_to_speech.run(model="s1")