Skip to content

Commit d8a429a

Browse files
authored
Merge pull request #45 from winzamark123/fix/audio
end points made with tests for audio
2 parents 9a04d0a + 6be8edd commit d8a429a

File tree

3 files changed

+163
-29
lines changed

3 files changed

+163
-29
lines changed

jigsawstack/audio.py

Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ class TextToSpeechParams(TypedDict):
1616
speaker_clone_file_store_key: NotRequired[str]
1717

1818

19+
class TTSCloneParams(TypedDict):
20+
url: NotRequired[str]
21+
file_store_key: NotRequired[str]
22+
name: str
23+
24+
25+
class GetTTSVoiceClonesParams(TypedDict):
26+
limit: NotRequired[int]
27+
page: NotRequired[int]
28+
29+
1930
class TextToSpeechResponse(TypedDict):
2031
success: bool
2132
text: str
@@ -70,12 +81,10 @@ def speech_to_text(self, params: SpeechToTextParams) -> SpeechToTextResponse: ..
7081
@overload
7182
def speech_to_text(self, file: bytes, options: Optional[SpeechToTextParams] = None) -> SpeechToTextResponse: ...
7283

73-
def speech_to_text(
74-
self,
75-
blob: Union[SpeechToTextParams, bytes],
76-
options: Optional[SpeechToTextParams] = None,
77-
) -> SpeechToTextResponse:
78-
if isinstance(blob, dict): # If params is provided as a dict, we assume it's the first argument
84+
def speech_to_text(self, blob: Union[SpeechToTextParams, bytes], options: Optional[SpeechToTextParams] = None) -> SpeechToTextResponse:
85+
if isinstance(
86+
blob, dict
87+
): # If params is provided as a dict, we assume it's the first argument
7988
resp = Request(
8089
config=self.config,
8190
path="/ai/transcribe",
@@ -89,17 +98,9 @@ def speech_to_text(
8998
content_type = options.get("content_type", "application/octet-stream")
9099
headers = {"Content-Type": content_type}
91100

92-
resp = Request(
93-
config=self.config,
94-
path=path,
95-
params=options,
96-
data=blob,
97-
headers=headers,
98-
verb="post",
99-
).perform_with_content()
101+
resp = Request(config=self.config, path=path, params=options, data=blob, headers=headers, verb="post").perform_with_content()
100102
return resp
101103

102-
103104
def text_to_speech(self, params: TextToSpeechParams) -> TextToSpeechResponse:
104105
path = "/ai/tts"
105106
resp = Request(
@@ -112,12 +113,23 @@ def text_to_speech(self, params: TextToSpeechParams) -> TextToSpeechResponse:
112113

113114
def speaker_voice_accents(self) -> TextToSpeechResponse:
114115
path = "/ai/tts"
115-
resp = Request(
116-
config=self.config,
117-
path=path,
118-
params={},
119-
verb="get",
120-
).perform_with_content()
116+
resp = Request(config=self.config, path=path, params={}, verb="get").perform_with_content()
117+
return resp
118+
119+
def create_clone(self, params: TTSCloneParams) -> TextToSpeechResponse:
120+
path = "/ai/tts/clone"
121+
resp = Request(config=self.config, path=path, params=cast(Dict[Any, Any], params), verb="post").perform_with_content()
122+
123+
return resp
124+
125+
def get_clones(self, params: GetTTSVoiceClonesParams) -> TextToSpeechResponse:
126+
path = "/ai/tts/clone"
127+
resp = Request(config=self.config, path=path, params=cast(Dict[Any, Any], params), verb="get").perform_with_content()
128+
return resp
129+
130+
def delete_clone(self, voice_id: str) -> TextToSpeechResponse:
131+
path = f"/ai/tts/clone/{voice_id}"
132+
resp = Request(config=self.config, path=path, params={}, verb="delete").perform_with_content()
121133
return resp
122134

123135

@@ -140,7 +152,9 @@ def __init__(
140152
@overload
141153
async def speech_to_text(self, params: SpeechToTextParams) -> SpeechToTextResponse: ...
142154
@overload
143-
async def speech_to_text(self, file: bytes, options: Optional[SpeechToTextParams] = None) -> SpeechToTextResponse: ...
155+
async def speech_to_text(
156+
self, file: bytes, options: Optional[SpeechToTextParams] = None
157+
) -> SpeechToTextResponse: ...
144158

145159
async def speech_to_text(
146160
self,
@@ -155,7 +169,7 @@ async def speech_to_text(
155169
verb="post",
156170
).perform_with_content()
157171
return resp
158-
172+
159173
options = options or {}
160174
path = build_path(base_path="/ai/transcribe", params=options)
161175
content_type = options.get("content_type", "application/octet-stream")
@@ -190,3 +204,33 @@ async def speaker_voice_accents(self) -> TextToSpeechResponse:
190204
verb="get",
191205
).perform_with_content()
192206
return resp
207+
208+
async def create_clone(self, params: TTSCloneParams) -> TextToSpeechResponse:
209+
path = "/ai/tts/clone"
210+
resp = await AsyncRequest(
211+
config=self.config,
212+
path=path,
213+
params=cast(Dict[Any, Any], params),
214+
verb="post"
215+
).perform_with_content()
216+
return resp
217+
218+
async def get_clones(self, params: GetTTSVoiceClonesParams) -> TextToSpeechResponse:
219+
path = "/ai/tts/clone"
220+
resp = await AsyncRequest(
221+
config=self.config,
222+
path=path,
223+
params=cast(Dict[Any, Any], params),
224+
verb="get"
225+
).perform_with_content()
226+
return resp
227+
228+
async def delete_clone(self, voice_id: str) -> TextToSpeechResponse:
229+
path = f"/ai/tts/clone/{voice_id}"
230+
resp = await AsyncRequest(
231+
config=self.config,
232+
path=path,
233+
params={},
234+
verb="delete"
235+
).perform_with_content()
236+
return resp

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
setup(
88
name="jigsawstack",
9-
version="0.2.2",
9+
version="0.2.3",
1010
description="JigsawStack - The AI SDK for Python",
1111
long_description=open("README.md", encoding="utf8").read(),
1212
long_description_content_type="text/markdown",

tests/test_audio.py

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,108 @@
55
import pytest
66
import asyncio
77
import logging
8+
from jigsawstack import AsyncJigsawStack
89

910
logging.basicConfig(level=logging.INFO)
1011
logger = logging.getLogger(__name__)
1112

1213

13-
def test_async_speaker_voice_accents_response():
14+
def test_text_to_speech():
15+
async def _test():
16+
client = AsyncJigsawStack()
17+
18+
"""Test converting text to speech"""
19+
try:
20+
response = await client.audio.text_to_speech(
21+
{
22+
"text": "Hello world, this is a test of the JigsawStack text to speech API."
23+
}
24+
)
25+
print("Text to speech response:", response)
26+
assert response["success"] == True
27+
28+
except Exception as e:
29+
print(f"Error in text_to_speech test: {e}")
30+
31+
asyncio.run(_test())
32+
33+
34+
def test_speaker_voice_accents():
1435
async def _test():
1536
client = AsyncJigsawStack()
37+
38+
"""Test getting available voice accents"""
39+
try:
40+
response = await client.audio.speaker_voice_accents()
41+
print("Speaker voice accents response:", response)
42+
assert response["success"] == True
43+
44+
except Exception as e:
45+
print(f"Error in speaker voice accents test: {e}")
46+
47+
48+
def test_create_clone():
49+
async def _test():
50+
client = AsyncJigsawStack()
51+
52+
"""Test creating a voice clone with URL"""
1653
try:
17-
result = await client.audio.speaker_voice_accents()
18-
assert result["success"] == True
19-
except JigsawStackError as e:
20-
pytest.fail(f"Unexpected JigsawStackError: {e}")
54+
audio_url = (
55+
"https://jigsawstack.com/audio/test.mp3" # Replace with an actual URL
56+
)
57+
clone_response_url = await client.audio.create_clone(
58+
{"url": audio_url, "name": "Test Voice Clone URL"}
59+
)
60+
61+
assert clone_response_url["success"] == True
62+
63+
clone_response_file_store_key = client.audio.create_clone(
64+
{
65+
"file_store_key": "hello_audio",
66+
"name": "Test Voice Clone File Store Key",
67+
}
68+
)
69+
70+
assert clone_response_file_store_key["success"] == True
71+
72+
except Exception as e:
73+
print(f"Error in voice_cloning test: {e}")
74+
75+
asyncio.run(_test())
76+
77+
78+
def test_get_clones():
79+
async def _test():
80+
client = AsyncJigsawStack()
81+
"""Test getting voice clones"""
82+
try:
83+
# List available voice clones
84+
clones_response = await client.audio.get_clones({"limit": 10, "page": 1})
85+
86+
assert clones_response["success"] == True
87+
88+
except Exception as e:
89+
print(f"Error in voice_cloning test: {e}")
90+
91+
asyncio.run(_test())
92+
93+
94+
def test_delete_clone():
95+
async def _test():
96+
client = AsyncJigsawStack()
97+
"""Test getting a voice clone"""
98+
try:
99+
create_clone_response = await client.audio.create_clone(
100+
{"name": "Test Voice Clone URL", "file_store_key": "hello_audio"}
101+
)
102+
clones = await client.audio.get_clones({"limit": 10, "page": 1})
103+
print("Clones:", clones)
104+
clone_id = clones["data"][0]["id"]
105+
delete_clone_response = await client.audio.delete_clone(clone_id)
106+
print("Delete clone response:", delete_clone_response)
107+
assert delete_clone_response["success"] == True
108+
109+
except Exception as e:
110+
print(f"Error in get_clone test: {e}")
21111

22112
asyncio.run(_test())

0 commit comments

Comments
 (0)